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