FastScriptReload
FastScriptReload copied to clipboard
Support partial classes
Addressing support partial classes
A MergePartials step is added, where syntax trees containing partial definitions get rebuilded to contain all partials found combined.
Steps done for it:
- Check if the input syntax tree contains any partial types.
- If partial types exist, process the current tree:
- Extract all using directives
- Collect all type declarations
- Search for other files with partial declarations in the same directory (recurive).
- Process each found file, collecting their type declarations and using directives.
- Combine all partial declarations of the same type.
- Create a new syntax tree with all usings, fields and methods
Sorry for delayed response - been away. Looking good - glad you've added UnitTests as well. Looking forward to reviewing.
Much needed feature btw. There's been quite a lot question about this one over time.
Thanks for contributing!
Hoping this will be merged soon! 🙏
Looks good - thanks again for contribution!
@RunninglVlan done some testing and merged to master. Looks good - if you have a chance to try do let us know how you got on.
@handzlikchris, it might not be related to this PR, but there are not many partial classes yet in our current project, so I'm sharing this finding. I have both interface and partial class in the same file and here's what FSR created: Was:
public interface IMenu {
// ...
}
public partial class Menu : MonoBehaviour, IMenu {
// ...
}
FSR generated (doesn't compile):
public interface IMenu__Patched_{
// ...
}
public partial class Menu__Patched_:MonoBehaviour,IMenu { // NOTE: Incorrect interface
// ...
}
Hmm, thanks - why does that not compile? Did you change interface? - btw would be good to start as a separate issue.
@Jlabarca Seems I understood what's the issue. It's not about both interface and partial class in one file. Partial class merging doesn't keep namespaces. I also noticed that tests don't check that namespace exists in the merged file, although test classes have them 😅 Without namespace it works with interface in the same file and without it.
Here's my test: Partial classes:
// TestClass1.1.cs
using TeamHalfBeard.Core;
using UnityEngine.InputSystem;
namespace TestNamespace {
[Service(typeof(TestClass1))]
public partial class TestClass1 : IUpdatable {
public void Update(float deltaTime) {
if (Keyboard.current.kKey.wasPressedThisFrame) {
LogMessage("kek");
}
}
}
}
// TestClass1.2.cs
using UnityEngine;
namespace TestNamespace {
public partial class TestClass1 {
private void LogMessage(string message) {
Debug.Log($"{GetType().Name}.{message}");
}
}
}
Partial classes (result):
using TeamHalfBeard.Core;
using UnityEngine.InputSystem;
using UnityEngine;
[assembly:System.Runtime.CompilerServices.InternalsVisibleTo("Assembly-CSharp")] [Service(typeof(TestClass1))]
public partial class TestClass1__Patched_:IUpdatable {
public void Update(float deltaTime) {
if (Keyboard.current.kKey.wasPressedThisFrame) {
LogMessage("lol");
}
}
private void LogMessage(string message) {
Debug.Log($"{GetType().Name}.{message}");
}
private static global::System.Collections.Generic.Dictionary<string, global::System.Func<object>> __Patched_NewFieldNameToInitialValueFn = new global::System.Collections.Generic.Dictionary<string, global::System.Func<object>>
{
};
private static global::System.Collections.Generic.Dictionary<string, global::System.Func<object>> __Patched_NewFieldsToGetTypeFnDictionaryFieldName = new global::System.Collections.Generic.Dictionary<string, global::System.Func<object>>
{
};
}
Normal class:
// TestClass2.cs
using TeamHalfBeard.Core;
using UnityEngine;
using UnityEngine.InputSystem;
namespace TestNamespace {
[Service(typeof(TestClass2))]
public class TestClass2 : IUpdatable {
public void Update(float deltaTime) {
if (Keyboard.current.kKey.wasPressedThisFrame) {
LogMessage("lol");
}
}
private void LogMessage(string message) {
Debug.Log($"{GetType().Name}.{message}");
}
}
}
Normal class (result):
using TeamHalfBeard.Core;
using UnityEngine;
using UnityEngine.InputSystem;
namespace TestNamespace {
[Service(typeof(TestClass2))]
public class TestClass2__Patched_: IUpdatable {
public void Update(float deltaTime) {
if (Keyboard.current.kKey.wasPressedThisFrame) {
LogMessage("lol");
}
}
private void LogMessage(string message) {
Debug.Log($"{GetType().Name}.{message}");
}
private static global::System.Collections.Generic.Dictionary<string, global::System.Func<object>> __Patched_NewFieldNameToInitialValueFn = new global::System.Collections.Generic.Dictionary<string, global::System.Func<object>>
{
};
private static global::System.Collections.Generic.Dictionary<string, global::System.Func<object>> __Patched_NewFieldsToGetTypeFnDictionaryFieldName = new global::System.Collections.Generic.Dictionary<string, global::System.Func<object>>
{
};
}
}
@handzlikchris, created #137