From 6b1bd4b3d95a5eaa1dd6fbbf89afb1ff57f69f64 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Fri, 10 Oct 2025 01:15:32 +0400 Subject: [PATCH 01/10] 1.1.2 Switch to Unity Window Only on MacOS --- gradle.properties | 2 +- .../WindowFocusSwitch.cs | 12 +++++------- src/rider/main/resources/META-INF/plugin.xml | 6 ++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/gradle.properties b/gradle.properties index 1daed19..3bb1766 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ DotnetPluginId=ReSharperPlugin.SerializeReferenceDropdownIntegration DotnetSolution=ReSharperPlugin.SerializeReferenceDropdownIntegration.sln RiderPluginId=com.jetbrains.rider.plugins.serializereferencedropdownintegration -PluginVersion=1.1.1 +PluginVersion=1.1.2 BuildConfiguration=Debug diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs index a542867..86685c8 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs @@ -1,5 +1,5 @@ -using System; using System.Diagnostics; +using System.Runtime.InteropServices; namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; @@ -7,11 +7,9 @@ public static class WindowFocusSwitch { public static void SwitchToUnityApplication() { - SwitchOnMacOS(); - } - - private static void SwitchOnMacOS() - { - Process.Start("osascript", "-e \"tell application \\\"Unity\\\" to activate\""); + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Process.Start("osascript", "-e \"tell application \\\"Unity\\\" to activate\""); + } } } \ No newline at end of file diff --git a/src/rider/main/resources/META-INF/plugin.xml b/src/rider/main/resources/META-INF/plugin.xml index a50f872..0660add 100644 --- a/src/rider/main/resources/META-INF/plugin.xml +++ b/src/rider/main/resources/META-INF/plugin.xml @@ -1,12 +1,10 @@ - + com.jetbrains.rider.plugins.serializereferencedropdownintegration SerializeReferenceDropdownIntegration - 1.1.1 + 1.1.2 Author Alexey Taranov com.intellij.modules.rider - Integration for unity package: SerializeReferenceDropdown

]]>
-
From e377cbeefb2b86e8321b020dc9fc7d155bec3452 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Sat, 11 Oct 2025 21:18:05 +0400 Subject: [PATCH 02/10] Messages replaced with logs --- .../Log.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Log.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Log.cs index fa090d6..6dc4bea 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Log.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Log.cs @@ -1,5 +1,5 @@ +using System; using System.Diagnostics; -using JetBrains.Util; namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; @@ -8,12 +8,12 @@ public class Log [Conditional("DEVLOG")] public static void DevInfo(string data) { - MessageBox.ShowInfo(data, "SRD DEV"); + Console.WriteLine($"SRD DEV: {data}"); } [Conditional("DEVLOG")] public static void DevError(string data) { - MessageBox.ShowInfo(data, "SRD DEV"); + Console.WriteLine($"SRD DEV ERROR: {data}"); } } \ No newline at end of file From 2429e1e1046e5878e632d494c442419556fda172 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Thu, 16 Oct 2025 21:52:11 +0400 Subject: [PATCH 03/10] Notify about last refresh with load package --- .../ClassUsageAnalyzer.cs | 2 +- .../DatabaseLoader.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs index 874e66d..a1a156e 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs @@ -39,7 +39,7 @@ private async void LoadDatabase() if (result == LoadResult.NoError) { var body = $"Loaded - {databaseLoader.TypesCount.Count} types \n" + - $"Last refresh: {databaseLoader.LastDatabaseUpdate}"; + $"Last refresh: {databaseLoader.DatabaseLastWriteTime}"; userNotifications.CreateNotification(lifetime, NotificationSeverity.INFO, "SRD - Database loaded", body, closeAfterExecution: true); diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs index 8f377a0..5a2e16c 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs @@ -26,8 +26,8 @@ public class DatabaseLoader private readonly ConcurrentDictionary typesCount = new(); - private static bool isRunningUpdate; private DateTime lastDatabaseUpdate; + private static bool isRunningUpdate; public DatabaseLoader(ISolution solution, Lifetime lifetime) { @@ -37,7 +37,8 @@ public DatabaseLoader(ISolution solution, Lifetime lifetime) public IReadOnlyDictionary TypesCount => typesCount; public bool IsAvailableDatabase { get; private set; } - public DateTime LastDatabaseUpdate => lastDatabaseUpdate; + public DateTime DatabaseLastWriteTime { get; private set; } + public async Task LoadDatabase() { @@ -94,6 +95,7 @@ private async Task UpdateDatabaseImpl(string jsonPath) try { lastDatabaseUpdate = DateTime.Now; + DatabaseLastWriteTime = File.GetLastWriteTime(jsonPath); await Task.Run(() => FillTypesFromPath(jsonPath), lifetime); ok = true; } @@ -136,6 +138,7 @@ private static void FindObjectTypes(JToken token, string propertyName, ref List< { values.Add(prop.Value.ToString()); } + FindObjectTypes(prop.Value, propertyName, ref values); } } From ba9db24bbdada3a9420829444e7704381c9af662 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Thu, 16 Oct 2025 22:38:00 +0400 Subject: [PATCH 04/10] Hide usages on non-reference types or unity object types --- .../ClassUsageAnalyzer.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs index a1a156e..627cbfc 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs @@ -63,6 +63,19 @@ protected override void Run(IClassDeclaration element, ElementProblemAnalyzerDat return; } + var nonReferenceType = element.IsStatic || element.IsAbstract; + if (nonReferenceType) + { + return; + } + + var superClassNames = element.DeclaredElement.GetAllSuperClasses().Select(t => t.GetClrName()); + var inheritedFromUnityObject = superClassNames.Any(t => t.FullName == "UnityEngine.Object"); + if (inheritedFromUnityObject) + { + return; + } + var clrName = element.DeclaredElement.GetClrName(); var name = clrName.FullName; var asmName = element.GetPsiModule().ContainingProjectModule.Name; From 5ef3f12bf2fac074e7d31108c1301421ef95758b Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Tue, 21 Oct 2025 21:37:47 +0400 Subject: [PATCH 05/10] Class Usage moved to folder --- .../{ => ClassUsage}/ClassUsageAnalyzer.cs | 14 ++++++++++++-- .../{ => ClassUsage}/ClassUsageInsightsProvider.cs | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) rename src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/{ => ClassUsage}/ClassUsageAnalyzer.cs (89%) rename src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/{ => ClassUsage}/ClassUsageInsightsProvider.cs (95%) diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs similarity index 89% rename from src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs rename to src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs index 627cbfc..50e2617 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageAnalyzer.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs @@ -1,15 +1,17 @@ using System; using System.Collections.Generic; +using System.Linq; using JetBrains.Application.DataContext; using JetBrains.Application.Notifications; +using JetBrains.Lifetimes; using JetBrains.ProjectModel; using JetBrains.ReSharper.Daemon.CodeInsights; using JetBrains.ReSharper.Feature.Services.Daemon; using JetBrains.ReSharper.Psi.CSharp.Tree; using JetBrains.ReSharper.Psi.Tree; -using JetBrains.Lifetimes; +using JetBrains.ReSharper.Psi.Util; -namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ClassUsage; [ElementProblemAnalyzer(typeof(IClassDeclaration))] public class ClassUsageAnalyzer : ElementProblemAnalyzer @@ -40,9 +42,17 @@ private async void LoadDatabase() { var body = $"Loaded - {databaseLoader.TypesCount.Count} types \n" + $"Last refresh: {databaseLoader.DatabaseLastWriteTime}"; + userNotifications.CreateNotification(lifetime, NotificationSeverity.INFO, "SRD - Database loaded", body, closeAfterExecution: true); + + if ((DateTime.Now - databaseLoader.DatabaseLastWriteTime).Days > 1) + { + userNotifications.CreateNotification(lifetime, NotificationSeverity.WARNING, + "SRD - Database need refresh?", + body, closeAfterExecution: true); + } } if (result == LoadResult.NoDatabaseFile) diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageInsightsProvider.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs similarity index 95% rename from src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageInsightsProvider.cs rename to src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs index cbe723d..508d90f 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsageInsightsProvider.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs @@ -4,7 +4,7 @@ using JetBrains.ReSharper.Daemon.CodeInsights; using JetBrains.Rider.Model; -namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ClassUsage; [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)] public class ClassUsageInsightsProvider : ICodeInsightsProvider From 52a92ecb1a87b732bd8ed1674e4f464dbf9aaae5 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Tue, 21 Oct 2025 22:30:56 +0400 Subject: [PATCH 06/10] Refactored - static replaced with solution components --- .../ClassUsage/ClassUsageAnalyzer.cs | 61 +++------- .../ClassUsage/ClassUsageInsightsProvider.cs | 17 ++- .../ToUnitySrdPipe.cs} | 18 +-- .../ToUnity/ToUnityWindowFocusSwitch.cs | 18 +++ .../TypeExtensions.cs | 9 ++ .../Unity/UnityProjectDetector.cs | 40 +++++++ .../UnitySrdDatabaseLoader.cs} | 113 ++++++++++++------ .../WindowFocusSwitch.cs | 15 --- 8 files changed, 181 insertions(+), 110 deletions(-) rename src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/{UnityBridge.cs => ToUnity/ToUnitySrdPipe.cs} (56%) create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnityWindowFocusSwitch.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/TypeExtensions.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs rename src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/{DatabaseLoader.cs => Unity/UnitySrdDatabaseLoader.cs} (63%) delete mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs index 50e2617..09aa066 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs @@ -2,8 +2,6 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Application.DataContext; -using JetBrains.Application.Notifications; -using JetBrains.Lifetimes; using JetBrains.ProjectModel; using JetBrains.ReSharper.Daemon.CodeInsights; using JetBrains.ReSharper.Feature.Services.Daemon; @@ -17,58 +15,29 @@ namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ClassUsage; public class ClassUsageAnalyzer : ElementProblemAnalyzer { private readonly ClassUsageInsightsProvider codeInsightsProvider; - private readonly UserNotifications userNotifications; - private readonly Lifetime lifetime; - private readonly DatabaseLoader databaseLoader; + private readonly UnitySrdDatabaseLoader unitySrdDatabaseLoader; + private readonly UnityProjectDetector unityProjectDetector; public static readonly Dictionary shortTypeToFullType = new(); - public ClassUsageAnalyzer(ClassUsageInsightsProvider codeInsightsProvider, UserNotifications userNotifications, - Lifetime lifetime, IDataContext context, ISolution solution) + public ClassUsageAnalyzer(ClassUsageInsightsProvider codeInsightsProvider, IDataContext context, ISolution solution, + UnitySrdDatabaseLoader unitySrdDatabaseLoader, UnityProjectDetector unityProjectDetector) { this.codeInsightsProvider = codeInsightsProvider; - this.userNotifications = userNotifications; - this.lifetime = lifetime; - databaseLoader = new DatabaseLoader(solution, this.lifetime); - LoadDatabase(); + this.unitySrdDatabaseLoader = unitySrdDatabaseLoader; + this.unityProjectDetector = unityProjectDetector; } - - private async void LoadDatabase() + + protected override void Run(IClassDeclaration element, ElementProblemAnalyzerData data, + IHighlightingConsumer consumer) { - Log.DevInfo("Start load database"); - var result = await databaseLoader.LoadDatabase(); - Log.DevInfo($"End load database: {result}"); - if (result == LoadResult.NoError) - { - var body = $"Loaded - {databaseLoader.TypesCount.Count} types \n" + - $"Last refresh: {databaseLoader.DatabaseLastWriteTime}"; - - userNotifications.CreateNotification(lifetime, NotificationSeverity.INFO, - "SRD - Database loaded", - body, closeAfterExecution: true); - - if ((DateTime.Now - databaseLoader.DatabaseLastWriteTime).Days > 1) - { - userNotifications.CreateNotification(lifetime, NotificationSeverity.WARNING, - "SRD - Database need refresh?", - body, closeAfterExecution: true); - } - } - - if (result == LoadResult.NoDatabaseFile) + if (unityProjectDetector.IsUnityProject() == false) { - userNotifications.CreateNotification(lifetime, NotificationSeverity.WARNING, - "SRD - No Database File", - "Need generate database file", closeAfterExecution: true); + return; } - } - - protected override void Run(IClassDeclaration element, ElementProblemAnalyzerData data, - IHighlightingConsumer consumer) - { - databaseLoader.UpdateDatabaseBackground(); - if (databaseLoader.IsAvailableDatabase == false) + unitySrdDatabaseLoader.RefreshDatabase(); + if (unitySrdDatabaseLoader.IsAvailableDatabase == false) { return; } @@ -89,8 +58,8 @@ protected override void Run(IClassDeclaration element, ElementProblemAnalyzerDat var clrName = element.DeclaredElement.GetClrName(); var name = clrName.FullName; var asmName = element.GetPsiModule().ContainingProjectModule.Name; - var type = DatabaseLoader.MakeType(name, asmName); - databaseLoader.TypesCount.TryGetValue(type, out var usageCount); + var type = TypeExtensions.MakeType(name, asmName); + unitySrdDatabaseLoader.TypesCount.TryGetValue(type, out var usageCount); shortTypeToFullType[clrName.ShortName] = type; var tooltip = $"SerializeReferenceDropdown: '{clrName.ShortName}' {usageCount} - usages in project"; diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs index 508d90f..63ed49d 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs @@ -3,22 +3,33 @@ using JetBrains.ProjectModel; using JetBrains.ReSharper.Daemon.CodeInsights; using JetBrains.Rider.Model; +using ReSharperPlugin.SerializeReferenceDropdownIntegration.ToUnity; namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ClassUsage; [SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)] public class ClassUsageInsightsProvider : ICodeInsightsProvider { + private readonly ToUnitySrdPipe toUnitySrdPipe; + private readonly ToUnityWindowFocusSwitch toUnityWindowFocusSwitch; + + public ClassUsageInsightsProvider(ToUnitySrdPipe toUnitySrdPipe, + ToUnityWindowFocusSwitch toUnityWindowFocusSwitch) + { + this.toUnitySrdPipe = toUnitySrdPipe; + this.toUnityWindowFocusSwitch = toUnityWindowFocusSwitch; + } + public bool IsAvailableIn(ISolution solution) { return true; } - public void OnClick(CodeInsightHighlightInfo highlightInfo, ISolution solution,CodeInsightsClickInfo clickInfo) + public void OnClick(CodeInsightHighlightInfo highlightInfo, ISolution solution, CodeInsightsClickInfo clickInfo) { var typeName = GetFullTypeName(highlightInfo); - UnityBridge.OpenUnitySearchToolWindowWithType(typeName); - WindowFocusSwitch.SwitchToUnityApplication(); + toUnitySrdPipe.OpenUnitySearchToolWindowWithType(typeName); + toUnityWindowFocusSwitch.SwitchToUnityApplication(); } private string GetFullTypeName(CodeInsightHighlightInfo highlightInfo) diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/UnityBridge.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnitySrdPipe.cs similarity index 56% rename from src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/UnityBridge.cs rename to src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnitySrdPipe.cs index efe3003..7817b56 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/UnityBridge.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnitySrdPipe.cs @@ -1,20 +1,23 @@ using System; using System.IO.Pipes; using System.Text; +using JetBrains.Application.Parts; +using JetBrains.ProjectModel; using JetBrains.Util; -namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ToUnity; -public static class UnityBridge +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] +public class ToUnitySrdPipe { - private const string pipeName = "SerializeReferenceDropdownIntegration"; - private static bool showOnce; + private const string PipeName = "SerializeReferenceDropdownIntegration"; + private bool showOnce; - public static void OpenUnitySearchToolWindowWithType(string typeName) + public void OpenUnitySearchToolWindowWithType(string typeName) { try { - using var client = new NamedPipeClientStream(".", pipeName, PipeDirection.Out); + using var client = new NamedPipeClientStream(".", PipeName, PipeDirection.Out); client.Connect(); var command = $"ShowSearchTypeWindow-{typeName}"; Log.DevInfo($"Send message: {command}"); @@ -23,14 +26,13 @@ public static void OpenUnitySearchToolWindowWithType(string typeName) client.Flush(); if (showOnce == false) { - MessageBox.ShowInfo("Check Unity app :)", "SRD DEV"); + MessageBox.ShowInfo("Check Unity window:)", "SRD DEV"); showOnce = true; } } catch (Exception e) { Log.DevError($"Send message failed: {e}"); - Console.WriteLine(e); throw; } } diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnityWindowFocusSwitch.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnityWindowFocusSwitch.cs new file mode 100644 index 0000000..f697534 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ToUnity/ToUnityWindowFocusSwitch.cs @@ -0,0 +1,18 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using JetBrains.Application.Parts; +using JetBrains.ProjectModel; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ToUnity; + +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] +public class ToUnityWindowFocusSwitch +{ + public void SwitchToUnityApplication() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Process.Start("osascript", "-e \"tell application \\\"Unity\\\" to activate\""); + } + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/TypeExtensions.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/TypeExtensions.cs new file mode 100644 index 0000000..3a94c7b --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/TypeExtensions.cs @@ -0,0 +1,9 @@ +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; + +public static class TypeExtensions +{ + public static string MakeType(string typeName, string asmName) + { + return $"{typeName},{asmName}".Replace(" ", ""); + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs new file mode 100644 index 0000000..371dc03 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs @@ -0,0 +1,40 @@ +using System.Linq; +using JetBrains.Application.Parts; +using JetBrains.ProjectModel; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; + +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] +public class UnityProjectDetector +{ + private readonly ISolution solution; + + //Can we update projects,assemblies, etc at runtime in current solution? + private bool? isUnityProject; + + public UnityProjectDetector(ISolution solution) + { + this.solution = solution; + } + + public bool IsUnityProject() + { + if (isUnityProject != null) + { + return isUnityProject.Value; + } + + foreach (var project in solution.GetAllProjects()) + { + var references = project.GetAllReferencedAssemblies().Select(r => r.Name); + if (references.Any(r => r.Contains("UnityEngine") || r.Contains("UnityEditor"))) + { + isUnityProject = true; + return true; + } + } + + isUnityProject = false; + return false; + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnitySrdDatabaseLoader.cs similarity index 63% rename from src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs rename to src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnitySrdDatabaseLoader.cs index 5a2e16c..1142561 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/DatabaseLoader.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnitySrdDatabaseLoader.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.IO; using System.Threading.Tasks; +using JetBrains.Application.Notifications; +using JetBrains.Application.Parts; using JetBrains.Lifetimes; using JetBrains.ProjectModel; using JetBrains.Util; @@ -10,37 +12,89 @@ namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; -public enum LoadResult +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] +public class UnitySrdDatabaseLoader { - NoError, - NoDatabaseFile, - NoSRDPackage, - ErrorLoading -} + private enum LoadResult + { + NoError, + NoDatabaseFile, + NoSRDPackage, + ErrorLoading + } -public class DatabaseLoader -{ - private readonly string databaseJsonName = "SerializeReference_ToolSearch_DataCacheFile.json"; - private readonly ISolution solution; + private const string DatabaseJsonName = "SerializeReference_ToolSearch_DataCacheFile.json"; + + private readonly UserNotifications userNotifications; private readonly Lifetime lifetime; + private readonly ISolution solution; private readonly ConcurrentDictionary typesCount = new(); private DateTime lastDatabaseUpdate; - private static bool isRunningUpdate; + private DateTime lastDatabaseWriteTime; + private bool isRunningUpdate; - public DatabaseLoader(ISolution solution, Lifetime lifetime) + public IReadOnlyDictionary TypesCount => typesCount; + public bool IsAvailableDatabase { get; private set; } + + public UnitySrdDatabaseLoader(UserNotifications userNotifications, Lifetime lifetime, ISolution solution, + UnityProjectDetector unityProjectDetector) { - this.solution = solution; + this.userNotifications = userNotifications; this.lifetime = lifetime; + this.solution = solution; + if (unityProjectDetector.IsUnityProject()) + { + LoadDatabase(); + } } - public IReadOnlyDictionary TypesCount => typesCount; - public bool IsAvailableDatabase { get; private set; } - public DateTime DatabaseLastWriteTime { get; private set; } + private string GetDatabaseJsonPath() + { + var jsonPath = Path.Combine(solution.SolutionDirectory.FullPath, "Library", DatabaseJsonName); + return jsonPath; + } + + private string GetPackagesJsonPath() + { + var jsonPath = Path.Combine(solution.SolutionDirectory.FullPath, "Packages", "packages-lock.json"); + return jsonPath; + } + + private async void LoadDatabase() + { + Log.DevInfo("Start load database"); + var result = await LoadDatabaseImpl(); + if (result == LoadResult.NoError) + { + var body = $"Loaded - {TypesCount.Count} types \n" + + $"Last refresh: {lastDatabaseWriteTime}"; + userNotifications.CreateNotification(lifetime, NotificationSeverity.INFO, + "SRD - Database loaded", + body, closeAfterExecution: true); - public async Task LoadDatabase() + if ((DateTime.Now - lastDatabaseWriteTime).Days > 1) + { + userNotifications.CreateNotification(lifetime, NotificationSeverity.WARNING, + "SRD - Database need refresh?", + body, closeAfterExecution: true); + } + } + + if (result == LoadResult.NoDatabaseFile) + { + userNotifications.CreateNotification(lifetime, NotificationSeverity.WARNING, + "SRD - No Database File", + "Need generate database file", closeAfterExecution: true); + } + + Log.DevInfo($"End load database: {result}"); + } + + + private async Task LoadDatabaseImpl() { var jsonPath = GetDatabaseJsonPath(); if (File.Exists(jsonPath) == false) @@ -76,18 +130,13 @@ private void FillTypesFromPath(string path) foreach (var allType in allTypes) { var array = allType.Split(','); - var type = MakeType(array[0], array[1]); + var type = TypeExtensions.MakeType(array[0], array[1]); typesCount.TryGetValue(type, out var value); value++; typesCount[type] = value; } } - public static string MakeType(string typeName, string asmName) - { - return $"{typeName},{asmName}".Replace(" ", ""); - } - private async Task UpdateDatabaseImpl(string jsonPath) { isRunningUpdate = true; @@ -95,7 +144,7 @@ private async Task UpdateDatabaseImpl(string jsonPath) try { lastDatabaseUpdate = DateTime.Now; - DatabaseLastWriteTime = File.GetLastWriteTime(jsonPath); + lastDatabaseWriteTime = File.GetLastWriteTime(jsonPath); await Task.Run(() => FillTypesFromPath(jsonPath), lifetime); ok = true; } @@ -110,7 +159,7 @@ private async Task UpdateDatabaseImpl(string jsonPath) //TODO: make better bg update - public async void UpdateDatabaseBackground() + public async void RefreshDatabase() { var jsonPath = GetDatabaseJsonPath(); if (File.Exists(jsonPath)) @@ -128,7 +177,7 @@ public async void UpdateDatabaseBackground() } } - private static void FindObjectTypes(JToken token, string propertyName, ref List values) + private void FindObjectTypes(JToken token, string propertyName, ref List values) { if (token.Type == JTokenType.Object) { @@ -150,16 +199,4 @@ private static void FindObjectTypes(JToken token, string propertyName, ref List< } } } - - private string GetDatabaseJsonPath() - { - var jsonPath = Path.Combine(solution.SolutionDirectory.FullPath, "Library", databaseJsonName); - return jsonPath; - } - - private string GetPackagesJsonPath() - { - var jsonPath = Path.Combine(solution.SolutionDirectory.FullPath, "Packages", "packages-lock.json"); - return jsonPath; - } } \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs deleted file mode 100644 index 86685c8..0000000 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/WindowFocusSwitch.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; - -public static class WindowFocusSwitch -{ - public static void SwitchToUnityApplication() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - Process.Start("osascript", "-e \"tell application \\\"Unity\\\" to activate\""); - } - } -} \ No newline at end of file From c6b4c9fad1eeb6869b2c216c0cf8b10b354f3eb9 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Sun, 26 Oct 2025 21:40:13 +0400 Subject: [PATCH 07/10] Rename Class - add MovedFrom attribute --- src/dotnet/Plugin.props | 5 + .../ClassUsage/ClassUsageAnalyzer.cs | 84 ++++---- .../ClassUsage/ClassUsageInsightsProvider.cs | 2 +- .../Rename/MovedFromAtomicRename.cs | 186 ++++++++++++++++++ .../Rename/MovedFromAtomicRenameFactory.cs | 45 +++++ .../Rename/MovedFromRefactoringPage.cs | 56 ++++++ .../Rename/MovedFromRenameModel.cs | 51 +++++ .../Unity/KnownTypes.cs | 11 ++ .../Unity/KnownTypesCache.cs | 32 +++ .../Unity/SRD/UnityProjectDetector.cs | 52 +++++ .../Unity/{ => SRD}/UnitySrdDatabaseLoader.cs | 11 +- .../Unity/UnityProjectDetector.cs | 40 ---- 12 files changed, 488 insertions(+), 87 deletions(-) create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRename.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRenameFactory.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRefactoringPage.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRenameModel.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypes.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypesCache.cs create mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnityProjectDetector.cs rename src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/{ => SRD}/UnitySrdDatabaseLoader.cs (95%) delete mode 100644 src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs diff --git a/src/dotnet/Plugin.props b/src/dotnet/Plugin.props index eccd83d..ae8bc4f 100644 --- a/src/dotnet/Plugin.props +++ b/src/dotnet/Plugin.props @@ -14,4 +14,9 @@ + + + + + diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs index 09aa066..f58113f 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageAnalyzer.cs @@ -1,16 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Application.DataContext; -using JetBrains.ProjectModel; using JetBrains.ReSharper.Daemon.CodeInsights; using JetBrains.ReSharper.Feature.Services.Daemon; using JetBrains.ReSharper.Psi.CSharp.Tree; using JetBrains.ReSharper.Psi.Tree; using JetBrains.ReSharper.Psi.Util; +using ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity.SRD; namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ClassUsage; +//TODO Move from problemAnalyzer to something else where usages works normally [ElementProblemAnalyzer(typeof(IClassDeclaration))] public class ClassUsageAnalyzer : ElementProblemAnalyzer { @@ -20,56 +20,64 @@ public class ClassUsageAnalyzer : ElementProblemAnalyzer public static readonly Dictionary shortTypeToFullType = new(); - public ClassUsageAnalyzer(ClassUsageInsightsProvider codeInsightsProvider, IDataContext context, ISolution solution, + public ClassUsageAnalyzer(ClassUsageInsightsProvider codeInsightsProvider, UnitySrdDatabaseLoader unitySrdDatabaseLoader, UnityProjectDetector unityProjectDetector) { this.codeInsightsProvider = codeInsightsProvider; this.unitySrdDatabaseLoader = unitySrdDatabaseLoader; this.unityProjectDetector = unityProjectDetector; } - + protected override void Run(IClassDeclaration element, ElementProblemAnalyzerData data, IHighlightingConsumer consumer) { - if (unityProjectDetector.IsUnityProject() == false) + try { - return; - } + if (unityProjectDetector.IsUnityProject() == false) + { + return; + } - unitySrdDatabaseLoader.RefreshDatabase(); - if (unitySrdDatabaseLoader.IsAvailableDatabase == false) - { - return; - } + unitySrdDatabaseLoader.RefreshDatabase(); + if (unitySrdDatabaseLoader.IsAvailableDatabase == false) + { + return; + } - var nonReferenceType = element.IsStatic || element.IsAbstract; - if (nonReferenceType) - { - return; - } + var nonReferenceType = element.IsStatic || element.IsAbstract; + if (nonReferenceType) + { + return; + } - var superClassNames = element.DeclaredElement.GetAllSuperClasses().Select(t => t.GetClrName()); - var inheritedFromUnityObject = superClassNames.Any(t => t.FullName == "UnityEngine.Object"); - if (inheritedFromUnityObject) - { - return; - } + var superClassNames = element.DeclaredElement.GetAllSuperClasses().Select(t => t.GetClrName()); + var inheritedFromUnityObject = superClassNames.Any(t => t.FullName == "UnityEngine.Object"); + if (inheritedFromUnityObject) + { + return; + } - var clrName = element.DeclaredElement.GetClrName(); - var name = clrName.FullName; - var asmName = element.GetPsiModule().ContainingProjectModule.Name; - var type = TypeExtensions.MakeType(name, asmName); - unitySrdDatabaseLoader.TypesCount.TryGetValue(type, out var usageCount); - shortTypeToFullType[clrName.ShortName] = type; + var clrName = element.DeclaredElement.GetClrName(); + var name = clrName.FullName; + var asmName = element.GetPsiModule().ContainingProjectModule.Name; + var type = TypeExtensions.MakeType(name, asmName); + unitySrdDatabaseLoader.TypesCount.TryGetValue(type, out var usageCount); + shortTypeToFullType[clrName.ShortName] = type; - var tooltip = $"SerializeReferenceDropdown: '{clrName.ShortName}' {usageCount} - usages in project"; - consumer.AddHighlighting( - new CodeInsightsHighlighting( - element.GetNameDocumentRange(), - displayText: $"SRD: {usageCount} usages", - tooltipText: tooltip, - moreText: String.Empty, - codeInsightsProvider, - element.DeclaredElement, null)); + //TODO Need check usages with MovedFrom attribute + var tooltip = $"SerializeReferenceDropdown: '{clrName.ShortName}' {usageCount} - usages in project"; + consumer.AddHighlighting( + new CodeInsightsHighlighting( + element.GetNameDocumentRange(), + displayText: $"SRD: {usageCount} usages", + tooltipText: tooltip, + moreText: String.Empty, + codeInsightsProvider, + element.DeclaredElement, null)); + } + catch (Exception e) + { + Log.DevError(e.ToString()); + } } } \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs index 63ed49d..d65cc5b 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/ClassUsage/ClassUsageInsightsProvider.cs @@ -7,7 +7,7 @@ namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.ClassUsage; -[SolutionComponent(Instantiation.ContainerAsyncPrimaryThread)] +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] public class ClassUsageInsightsProvider : ICodeInsightsProvider { private readonly ToUnitySrdPipe toUnitySrdPipe; diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRename.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRename.cs new file mode 100644 index 0000000..409590c --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRename.cs @@ -0,0 +1,186 @@ +using System.Collections.Generic; +using JetBrains.Application.Progress; +using JetBrains.Diagnostics; +using JetBrains.ReSharper.Feature.Services.Refactorings; +using JetBrains.ReSharper.Feature.Services.Refactorings.Specific.Rename; +using JetBrains.ReSharper.Psi; +using JetBrains.ReSharper.Psi.CSharp; +using JetBrains.ReSharper.Psi.CSharp.Impl; +using JetBrains.ReSharper.Psi.CSharp.Tree; +using JetBrains.ReSharper.Psi.Pointers; +using JetBrains.ReSharper.Psi.Tree; +using JetBrains.ReSharper.Refactorings.Rename; +using JetBrains.Util; +using JetBrains.Util.dataStructures; +using ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Refactorings.Rename; + +public class MovedFromAtomicRename : AtomicRenameBase +{ + private readonly KnownTypesCache myKnownTypesCache; + private readonly IDeclaredElementPointer myPointer; + private readonly MovedFromRenameModel myModel; + + public MovedFromAtomicRename(IDeclaredElement declaredElement, string newName, + KnownTypesCache knownTypesCache) + { + myKnownTypesCache = knownTypesCache; + NewName = newName; + OldName = declaredElement.ShortName; + myPointer = declaredElement.CreateElementPointer(); + myModel = new MovedFromRenameModel(); + } + + public override IRefactoringPage CreateRenamesConfirmationPage(IRenameWorkflow renameWorkflow, + IProgressIndicator pi) + { + // hide confirmation page only, refactoring should update shared document too otherwise + // we will get inconsistent change modification message box + if (myModel.MovedFromRefactoringBehavior + is MovedFromRefactoringBehavior.AddAndRemember + or MovedFromRefactoringBehavior.DontAddAndRemember) + return null; + + return new MovedFromRefactoringPage( + ((RefactoringWorkflowBase)renameWorkflow).WorkflowExecuterLifetime, myModel, OldName); + } + + public override void Rename(IRenameRefactoring executer, IProgressIndicator pi, bool hasConflictsWithDeclarations, + IRefactoringDriver driver, PreviousAtomicRenames previousAtomicRenames) + { + if (myModel.MovedFromRefactoringBehavior + is MovedFromRefactoringBehavior.DontAdd + or MovedFromRefactoringBehavior.DontAddAndRemember) + return; + + var classMemberDeclaration = GetDeclaration(myPointer.FindDeclaredElement() as ITypeMember); + if (classMemberDeclaration == null) + return; + + //TODO Ask about remove old attribute? We can't use together two or more MovedFrom attributes + // RemoveExistingAttributesWithNewName(classMemberDeclaration); + + if (HasExistingMovedFromAttribute(classMemberDeclaration)) + { + // Make sure textual occurrence rename doesn't rename the existing attribute parameter + RemoveFromTextualOccurrences(executer, classMemberDeclaration); + return; + } + + //TODO Make rename source namespaces? + var attribute = CreateMovedFromAttribute(classMemberDeclaration, oldClassName: OldName); + if (attribute != null) + classMemberDeclaration.AddAttributeAfter(attribute, null); + } + + private void RemoveExistingAttributesWithNewName(IClassMemberDeclaration classMemberDeclaration) + { + var attributes = GetExistingFormerlySerializedAsAttributes(classMemberDeclaration, NewName); + foreach (var attribute in attributes) + classMemberDeclaration.RemoveAttribute(attribute); + } + + private static IClassMemberDeclaration? GetDeclaration(ITypeMember? typeMember) + { + var declarations = typeMember?.GetDeclarations(); + if (declarations?.Count == 1) + return declarations[0] as IClassMemberDeclaration; + return null; + } + + private bool HasExistingMovedFromAttribute(IClassMemberDeclaration classMemberDeclaration) + { + var attributes = GetExistingFormerlySerializedAsAttributes(classMemberDeclaration, OldName); + return attributes.Count > 0; + } + + private FrugalLocalList GetExistingFormerlySerializedAsAttributes( + IClassMemberDeclaration fieldDeclaration, string nameArgument) + { + var list = new FrugalLocalList(); + foreach (var attribute in fieldDeclaration.AttributesEnumerable) + { + var attributeTypeElement = attribute.TypeReference?.Resolve().DeclaredElement as ITypeElement; + if (attributeTypeElement == null) + continue; + + if (Equals(attributeTypeElement.GetClrName(), KnownTypes.MovedFromAttribute)) + { + var attributeInstance = attribute.GetAttributeInstance(); + var nameParameter = attributeInstance.PositionParameter(0); + if (nameParameter.IsConstant && nameParameter.ConstantValue.IsString(out var stringValue) && + stringValue == nameArgument) + { + list.Add(attribute); + } + } + } + + return list; + } + + private void RemoveFromTextualOccurrences(IRenameRefactoring executor, IClassMemberDeclaration fieldDeclaration) + { + if (executor.Workflow is not RenameWorkflow workflow) + return; + + var attributes = fieldDeclaration.Attributes; + if (attributes.Count == 0) + return; + + var attribute = attributes[0]; + var attributeSectionList = AttributeSectionListNavigator.GetByAttribute(attribute); + if (attributeSectionList == null) + return; + + var attributesRange = attributeSectionList.GetDocumentRange(); + + foreach (var occurrence in workflow.DataModel.ActualOccurrences ?? + EmptyList.InstanceList) + { + if (!occurrence.Included) + continue; + + + var occurrenceRange = occurrence.Marker.DocumentRange; + if (attributesRange.Contains(occurrenceRange)) + { + occurrence.Included = false; + break; + } + } + } + + private IAttribute? CreateMovedFromAttribute(IClassMemberDeclaration owningNode, + string oldClassName = null, string oldNamespace = null) + { + var module = owningNode.GetPsiModule(); + var elementFactory = CSharpElementFactory.GetInstance(owningNode); + var attributeType = myKnownTypesCache.GetByClrTypeName(KnownTypes.MovedFromAttribute, module); + var attributeTypeElement = attributeType.GetTypeElement(); + if (attributeTypeElement == null) + return null; + + var movedFromArguments = new AttributeValue[] + { + new(ConstantValue.Bool(true, module)), + new(ConstantValue.String(oldNamespace, module)), + new(ConstantValue.String(null, module)), + new(ConstantValue.String(oldClassName, module)) + }; + + + var movedFromAttribute = elementFactory.CreateAttribute(attributeTypeElement, + movedFromArguments, + EmptyArray>.Instance); + + return movedFromAttribute; + } + + public override IDeclaredElement NewDeclaredElement => myPointer.FindDeclaredElement().NotNull(); + public override string NewName { get; } + public override string OldName { get; } + public override IDeclaredElement PrimaryDeclaredElement => myPointer.FindDeclaredElement().NotNull(); + public override IList SecondaryDeclaredElements => null; +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRenameFactory.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRenameFactory.cs new file mode 100644 index 0000000..1339692 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromAtomicRenameFactory.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using JetBrains.Application; +using JetBrains.ProjectModel; +using JetBrains.ReSharper.Feature.Services.Refactorings.Specific.Rename; +using JetBrains.ReSharper.Psi; +using JetBrains.ReSharper.Psi.VB.Util; +using ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity; +using ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity.SRD; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Refactorings.Rename; + +[ShellFeaturePart] +public class MovedFromAtomicRenameFactory : IAtomicRenameFactory +{ + //TODO HACK I don't know why IsApplicable() called twice( + private static List _renameDeclaredElements = new(); + + public bool IsApplicable(IDeclaredElement declaredElement) + { + _renameDeclaredElements.Clear(); + + //TODO Support rename namespaces? + var isClass = declaredElement.IsClass(); + var isUnityProject = UnityProjectDetector.Instance.IsUnityProject(); + return isClass && isUnityProject; + } + + public RenameAvailabilityCheckResult CheckRenameAvailability(IDeclaredElement element) + { + return RenameAvailabilityCheckResult.CanBeRenamed; + } + + public IEnumerable CreateAtomicRenames(IDeclaredElement declaredElement, string newName, + bool doNotAddBindingConflicts) + { + if (_renameDeclaredElements.Contains(declaredElement.ShortName)) + { + return []; + } + + _renameDeclaredElements.Add(declaredElement.ShortName); + var knownTypesCache = declaredElement.GetSolution().GetComponent(); + return [new MovedFromAtomicRename(declaredElement, newName, knownTypesCache)]; + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRefactoringPage.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRefactoringPage.cs new file mode 100644 index 0000000..d1dbcd4 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRefactoringPage.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using JetBrains.DataFlow; +using JetBrains.IDE.UI.Extensions; +using JetBrains.Lifetimes; +using JetBrains.ReSharper.Feature.Services.Refactorings; +using JetBrains.Rider.Model.UIAutomation; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Refactorings.Rename; + +public class MovedFromRefactoringPage : SingleBeRefactoringPage +{ + private readonly MovedFromRenameModel myModel; + private readonly BeGrid myContent; + private readonly IProperty myShouldAddFormerlySerializedAs; + private readonly IProperty myRememberSelectedOptionAndNeverShowPopup; + + // TODO Replace strings with resourceManager? + public MovedFromRefactoringPage(Lifetime lifetime, + MovedFromRenameModel model, string oldName) : base(lifetime) + { + myModel = model; + + myShouldAddFormerlySerializedAs = new Property("Should add attribute action", + model.MovedFromRefactoringBehavior is MovedFromRefactoringBehavior.Add + or MovedFromRefactoringBehavior.AddAndRemember); + + + myContent = myShouldAddFormerlySerializedAs.GetBeRadioGroup(lifetime, + $"Add attribute MovedFrom to class: {oldName}", + new List { true, false }, + present: (settings, properties) => settings ? "Add" : "Don't Add", + horizontal: false + ).InAutoGrid(); + + + myRememberSelectedOptionAndNeverShowPopup = new Property("Save settings for this session", + model.MovedFromRefactoringBehavior + is MovedFromRefactoringBehavior.AddAndRemember + or MovedFromRefactoringBehavior.DontAddAndRemember); + myContent.AddElement(new BeSpacer()); + myContent.AddElement(myRememberSelectedOptionAndNeverShowPopup.GetBeCheckBox(lifetime, "Remember settings")); + } + + public override BeControl GetPageContent() => myContent; + + public override void Commit() + { + var shouldAdd = myShouldAddFormerlySerializedAs.Value; + var rememberSelectedOption = myRememberSelectedOptionAndNeverShowPopup?.Value ?? false; + + myModel.Commit(shouldAdd, rememberSelectedOption); + } + + public override string Title => "Rename with MovedFrom unity Attribute"; + public override string Description => "Renaming a class can break serialize references to this class"; +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRenameModel.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRenameModel.cs new file mode 100644 index 0000000..92796f7 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Refactorings/Rename/MovedFromRenameModel.cs @@ -0,0 +1,51 @@ +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Refactorings.Rename; + +public enum MovedFromRefactoringBehavior +{ + Add, + DontAdd, + AddAndRemember, + DontAddAndRemember, +} + +public enum MovedFromRefactoringSettings +{ + ShowPopup, + AlwaysAdd, + NeverAdd, +} + +public class MovedFromRenameModel +{ + //TODO Setup settings? + private static MovedFromRefactoringSettings ShowPopupSettings; + public MovedFromRefactoringBehavior MovedFromRefactoringBehavior { get; private set; } + + public MovedFromRenameModel() + { + MovedFromRefactoringBehavior = ShowPopupSettings switch + { + MovedFromRefactoringSettings.AlwaysAdd => MovedFromRefactoringBehavior.AddAndRemember, + MovedFromRefactoringSettings.NeverAdd => MovedFromRefactoringBehavior.DontAddAndRemember, + _ => MovedFromRefactoringBehavior.Add + }; + } + + public void Commit(bool shouldAddFormerlySerializedAs, bool rememberSelectedOptionAndNeverShowPopup) + { + MovedFromRefactoringBehavior = shouldAddFormerlySerializedAs + ? rememberSelectedOptionAndNeverShowPopup + ? MovedFromRefactoringBehavior.AddAndRemember + : MovedFromRefactoringBehavior.Add + : rememberSelectedOptionAndNeverShowPopup + ? MovedFromRefactoringBehavior.DontAddAndRemember + : MovedFromRefactoringBehavior.DontAdd; + + ShowPopupSettings = MovedFromRefactoringBehavior switch + { + MovedFromRefactoringBehavior.AddAndRemember => MovedFromRefactoringSettings.AlwaysAdd, + MovedFromRefactoringBehavior.DontAddAndRemember => MovedFromRefactoringSettings.NeverAdd, + _ => MovedFromRefactoringSettings.ShowPopup + }; + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypes.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypes.cs new file mode 100644 index 0000000..47763c0 --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypes.cs @@ -0,0 +1,11 @@ +using JetBrains.Metadata.Reader.API; +using JetBrains.Metadata.Reader.Impl; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity; + +public class KnownTypes +{ + // UnityEngine.Serialization + public static readonly IClrTypeName MovedFromAttribute = + new ClrTypeName("UnityEngine.Scripting.APIUpdating.MovedFromAttribute"); +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypesCache.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypesCache.cs new file mode 100644 index 0000000..09ca34e --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/KnownTypesCache.cs @@ -0,0 +1,32 @@ +using System.Collections.Concurrent; +using JetBrains.Application.Parts; +using JetBrains.Metadata.Reader.API; +using JetBrains.ProjectModel; +using JetBrains.ReSharper.Psi; +using JetBrains.ReSharper.Psi.Modules; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity; + +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] +public class KnownTypesCache +{ + private readonly ConcurrentDictionary myTypes = new(); + + public IDeclaredType GetByClrTypeName(IClrTypeName typeName, IPsiModule module) + { + // TODO: If/when Unity support nullability, add this as a parameter, and as a key to the cache + const NullableAnnotation nullableAnnotation = NullableAnnotation.Unknown; + + var type = module.GetPredefinedType().TryGetType(typeName, nullableAnnotation); + if (type != null) + return type; + + // Make sure the type is still valid before handing it out. It might be invalid if the module used to create + // it has been changed + type = myTypes.AddOrUpdate(typeName, name => TypeFactory.CreateTypeByCLRName(name, nullableAnnotation, module), + (name, existingValue) => existingValue.Module.IsValid() + ? existingValue + : TypeFactory.CreateTypeByCLRName(name, nullableAnnotation, module)); + return type; + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnityProjectDetector.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnityProjectDetector.cs new file mode 100644 index 0000000..5bebc6d --- /dev/null +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnityProjectDetector.cs @@ -0,0 +1,52 @@ +using System; +using System.Linq; +using JetBrains.Application.Parts; +using JetBrains.ProjectModel; + +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity.SRD; + +[SolutionComponent(Instantiation.DemandAnyThreadSafe)] +public class UnityProjectDetector +{ + private readonly ISolution solution; + + //Can we update projects,assemblies, etc at runtime in current solution? + private bool? isUnityProject; + + public static UnityProjectDetector Instance { get; private set; } + + public UnityProjectDetector(ISolution solution) + { + this.solution = solution; + Instance = this; + } + + public bool IsUnityProject() + { + if (isUnityProject != null) + { + return isUnityProject.Value; + } + + try + { + foreach (var project in solution.GetAllProjects()) + { + var references = project.GetAllReferencedAssemblies().Select(r => r.Name); + if (references.Any(r => r.Contains("UnityEngine") || r.Contains("UnityEditor"))) + { + isUnityProject = true; + return true; + } + } + + isUnityProject = false; + } + catch (Exception e) + { + Log.DevError(e.ToString()); + } + + return false; + } +} \ No newline at end of file diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnitySrdDatabaseLoader.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnitySrdDatabaseLoader.cs similarity index 95% rename from src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnitySrdDatabaseLoader.cs rename to src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnitySrdDatabaseLoader.cs index 1142561..d184233 100644 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnitySrdDatabaseLoader.cs +++ b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/SRD/UnitySrdDatabaseLoader.cs @@ -10,7 +10,7 @@ using JetBrains.Util; using Newtonsoft.Json.Linq; -namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; +namespace ReSharperPlugin.SerializeReferenceDropdownIntegration.Unity.SRD; [SolutionComponent(Instantiation.DemandAnyThreadSafe)] public class UnitySrdDatabaseLoader @@ -22,7 +22,7 @@ private enum LoadResult NoSRDPackage, ErrorLoading } - + private const string DatabaseJsonName = "SerializeReference_ToolSearch_DataCacheFile.json"; private readonly UserNotifications userNotifications; @@ -38,16 +38,11 @@ private enum LoadResult public IReadOnlyDictionary TypesCount => typesCount; public bool IsAvailableDatabase { get; private set; } - public UnitySrdDatabaseLoader(UserNotifications userNotifications, Lifetime lifetime, ISolution solution, - UnityProjectDetector unityProjectDetector) + public UnitySrdDatabaseLoader(UserNotifications userNotifications, Lifetime lifetime, ISolution solution) { this.userNotifications = userNotifications; this.lifetime = lifetime; this.solution = solution; - if (unityProjectDetector.IsUnityProject()) - { - LoadDatabase(); - } } private string GetDatabaseJsonPath() diff --git a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs b/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs deleted file mode 100644 index 371dc03..0000000 --- a/src/dotnet/ReSharperPlugin.SerializeReferenceDropdownIntegration/Unity/UnityProjectDetector.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Linq; -using JetBrains.Application.Parts; -using JetBrains.ProjectModel; - -namespace ReSharperPlugin.SerializeReferenceDropdownIntegration; - -[SolutionComponent(Instantiation.DemandAnyThreadSafe)] -public class UnityProjectDetector -{ - private readonly ISolution solution; - - //Can we update projects,assemblies, etc at runtime in current solution? - private bool? isUnityProject; - - public UnityProjectDetector(ISolution solution) - { - this.solution = solution; - } - - public bool IsUnityProject() - { - if (isUnityProject != null) - { - return isUnityProject.Value; - } - - foreach (var project in solution.GetAllProjects()) - { - var references = project.GetAllReferencedAssemblies().Select(r => r.Name); - if (references.Any(r => r.Contains("UnityEngine") || r.Contains("UnityEditor"))) - { - isUnityProject = true; - return true; - } - } - - isUnityProject = false; - return false; - } -} \ No newline at end of file From 49e3ac77afecfe7633e6425fc93164072070f749 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Thu, 6 Nov 2025 22:17:53 +0400 Subject: [PATCH 08/10] Github action - release package Chatgpt generated =) --- .github/workflows/release.yml | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2a7b321 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: Release Rider Plugin + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # needed for changelog and tagging + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + ${{ runner.os }}-gradle- + + # Auto version + changelog generator + - name: Bump version and generate changelog + id: changelog + uses: release-please/action@v4 + with: + release-type: simple + package-name: rider-plugin + changelog-types: | + [ + {"type":"feat","section":"โœจ Features","hidden":false}, + {"type":"fix","section":"๐Ÿ› Fixes","hidden":false}, + {"type":"chore","section":"๐Ÿงน Chores","hidden":false}, + {"type":"docs","section":"๐Ÿ“š Docs","hidden":false} + ] + + - name: Stop if no release created + if: ${{ steps.changelog.outputs.release_created != 'true' }} + run: echo "No new version to release." + + - name: Set version in gradle.properties + if: ${{ steps.changelog.outputs.release_created == 'true' }} + run: | + sed -i.bak "s/^version=.*/version=${{ steps.changelog.outputs.tag_name#v }}/" gradle.properties + cat gradle.properties + + - name: Build plugin + if: ${{ steps.changelog.outputs.release_created == 'true' }} + run: ./gradlew buildPlugin + + - name: Create GitHub Release + if: ${{ steps.changelog.outputs.release_created == 'true' }} + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.changelog.outputs.tag_name }} + name: Release ${{ steps.changelog.outputs.tag_name }} + body: ${{ steps.changelog.outputs.release_notes }} + files: build/distributions/*.zip + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From dc7b6698cc791ec6c1c1277b42df760878dd6604 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Thu, 6 Nov 2025 22:20:50 +0400 Subject: [PATCH 09/10] Refactor release workflow for better versioning Updated the release workflow to improve version handling and changelog generation. --- .github/workflows/release.yml | 54 +++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a7b321..15a5fc9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: - main permissions: - contents: write + contents: write # allows committing and creating releases jobs: release: @@ -16,7 +16,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 # needed for changelog and tagging + fetch-depth: 0 # needed for release-please - name: Set up JDK uses: actions/setup-java@v4 @@ -34,8 +34,8 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - # Auto version + changelog generator - - name: Bump version and generate changelog + # ๐Ÿง  Detect version bump and changelog + - name: Determine new version and changelog id: changelog uses: release-please/action@v4 with: @@ -48,21 +48,59 @@ jobs: {"type":"chore","section":"๐Ÿงน Chores","hidden":false}, {"type":"docs","section":"๐Ÿ“š Docs","hidden":false} ] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Stop if no release created + - name: Stop if no new release if: ${{ steps.changelog.outputs.release_created != 'true' }} run: echo "No new version to release." - - name: Set version in gradle.properties + # ๐Ÿงพ Update gradle.properties version + - name: Update gradle.properties version + if: ${{ steps.changelog.outputs.release_created == 'true' }} + run: | + TAG_NAME="${{ steps.changelog.outputs.tag_name }}" + VERSION="${TAG_NAME#v}" # remove leading 'v' + sed -i.bak "s/^version=.*/version=${VERSION}/" gradle.properties + echo "โœ… Updated gradle.properties to version ${VERSION}" + + # ๐Ÿงฉ Update plugin.xml version (safe for comments and multiple tags) + - name: Update plugin.xml version tags if: ${{ steps.changelog.outputs.release_created == 'true' }} run: | - sed -i.bak "s/^version=.*/version=${{ steps.changelog.outputs.tag_name#v }}/" gradle.properties - cat gradle.properties + TAG_NAME="${{ steps.changelog.outputs.tag_name }}" + VERSION="${TAG_NAME#v}" + XML_FILE="src/rider/main/resources/META-INF/plugin.xml" + echo "๐Ÿงฉ Updating ${XML_FILE} to version ${VERSION}" + awk -v ver="$VERSION" ' + // { in_comment=0 } + !in_comment { + gsub(/[^<]+<\/version>/, "" ver "") + } + { print } + ' "$XML_FILE" > "$XML_FILE.tmp" && mv "$XML_FILE.tmp" "$XML_FILE" + + echo "โœ… Updated version tags in plugin.xml:" + grep "" "$XML_FILE" || echo "(none found)" + + # ๐Ÿ—๏ธ Build Rider plugin - name: Build plugin if: ${{ steps.changelog.outputs.release_created == 'true' }} run: ./gradlew buildPlugin + # ๐Ÿ’พ Commit updated version and changelog + - name: Commit updated version and changelog + if: ${{ steps.changelog.outputs.release_created == 'true' }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add gradle.properties CHANGELOG.md src/rider/main/resources/META-INF/plugin.xml + git commit -m "chore(release): update version to ${{ steps.changelog.outputs.tag_name }}" + git push + + # ๐Ÿš€ Create GitHub Release with built ZIP - name: Create GitHub Release if: ${{ steps.changelog.outputs.release_created == 'true' }} uses: softprops/action-gh-release@v2 From aa8c33a8040c2a57f48f4948d9b26c76f0eca704 Mon Sep 17 00:00:00 2001 From: Alexey Taranov Date: Thu, 6 Nov 2025 22:23:42 +0400 Subject: [PATCH 10/10] Refactor release workflow for Rider Plugin Updated the GitHub Actions workflow for building and releasing the Rider Plugin. Added a new build job and improved version handling. --- .github/workflows/release.yml | 50 ++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15a5fc9..4f6ec7a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,22 +1,26 @@ -name: Release Rider Plugin +name: Build & Release Rider Plugin on: push: branches: - main + pull_request: + branches: + - main permissions: - contents: write # allows committing and creating releases + contents: write jobs: - release: + build: runs-on: ubuntu-latest + name: Build Plugin (PR / Main) steps: - name: Checkout code uses: actions/checkout@v4 with: - fetch-depth: 0 # needed for release-please + fetch-depth: 0 - name: Set up JDK uses: actions/setup-java@v4 @@ -34,7 +38,33 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - # ๐Ÿง  Detect version bump and changelog + - name: Build plugin + run: ./gradlew buildPlugin + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: plugin-build + path: build/distributions/*.zip + + release: + needs: build + runs-on: ubuntu-latest + name: Release on Main + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + - name: Determine new version and changelog id: changelog uses: release-please/action@v4 @@ -55,17 +85,15 @@ jobs: if: ${{ steps.changelog.outputs.release_created != 'true' }} run: echo "No new version to release." - # ๐Ÿงพ Update gradle.properties version - name: Update gradle.properties version if: ${{ steps.changelog.outputs.release_created == 'true' }} run: | TAG_NAME="${{ steps.changelog.outputs.tag_name }}" - VERSION="${TAG_NAME#v}" # remove leading 'v' + VERSION="${TAG_NAME#v}" sed -i.bak "s/^version=.*/version=${VERSION}/" gradle.properties echo "โœ… Updated gradle.properties to version ${VERSION}" - # ๐Ÿงฉ Update plugin.xml version (safe for comments and multiple tags) - - name: Update plugin.xml version tags + - name: Update plugin.xml version if: ${{ steps.changelog.outputs.release_created == 'true' }} run: | TAG_NAME="${{ steps.changelog.outputs.tag_name }}" @@ -82,15 +110,12 @@ jobs: { print } ' "$XML_FILE" > "$XML_FILE.tmp" && mv "$XML_FILE.tmp" "$XML_FILE" - echo "โœ… Updated version tags in plugin.xml:" grep "" "$XML_FILE" || echo "(none found)" - # ๐Ÿ—๏ธ Build Rider plugin - name: Build plugin if: ${{ steps.changelog.outputs.release_created == 'true' }} run: ./gradlew buildPlugin - # ๐Ÿ’พ Commit updated version and changelog - name: Commit updated version and changelog if: ${{ steps.changelog.outputs.release_created == 'true' }} run: | @@ -100,7 +125,6 @@ jobs: git commit -m "chore(release): update version to ${{ steps.changelog.outputs.tag_name }}" git push - # ๐Ÿš€ Create GitHub Release with built ZIP - name: Create GitHub Release if: ${{ steps.changelog.outputs.release_created == 'true' }} uses: softprops/action-gh-release@v2