[Add] Hot Reload
This commit is contained in:
@@ -0,0 +1,608 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using SingularityGroup.HotReload.DTO;
|
||||
using SingularityGroup.HotReload.Localization;
|
||||
using SingularityGroup.HotReload.Newtonsoft.Json;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using Translations = SingularityGroup.HotReload.Editor.Localization.Translations;
|
||||
|
||||
namespace SingularityGroup.HotReload.Editor {
|
||||
internal enum TimelineType {
|
||||
Suggestions,
|
||||
Timeline,
|
||||
}
|
||||
|
||||
internal enum AlertType {
|
||||
Suggestion,
|
||||
UnsupportedChange,
|
||||
CompileError,
|
||||
PartiallySupportedChange,
|
||||
AppliedChange,
|
||||
UndetectedChange,
|
||||
}
|
||||
|
||||
internal enum AlertEntryType {
|
||||
Error,
|
||||
Failure,
|
||||
InlinedMethod,
|
||||
PatchApplied,
|
||||
PartiallySupportedChange,
|
||||
UndetectedChange,
|
||||
}
|
||||
|
||||
internal enum EntryType {
|
||||
Parent,
|
||||
Child,
|
||||
Standalone,
|
||||
Foldout,
|
||||
}
|
||||
|
||||
internal class PersistedAlertData {
|
||||
public readonly AlertData[] alertDatas;
|
||||
|
||||
public PersistedAlertData(AlertData[] alertDatas) {
|
||||
this.alertDatas = alertDatas;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AlertData {
|
||||
public readonly AlertEntryType alertEntryType;
|
||||
public readonly string errorString;
|
||||
public readonly string methodName;
|
||||
public readonly string methodSimpleName;
|
||||
public readonly PartiallySupportedChange partiallySupportedChange;
|
||||
public readonly EntryType entryType;
|
||||
public readonly bool detiled;
|
||||
public readonly DateTime createdAt;
|
||||
public readonly string[] patchedMembersDisplayNames;
|
||||
|
||||
public AlertData(AlertEntryType alertEntryType, DateTime createdAt, bool detiled = false, EntryType entryType = EntryType.Standalone, string errorString = null, string methodName = null, string methodSimpleName = null, PartiallySupportedChange partiallySupportedChange = default(PartiallySupportedChange), string[] patchedMembersDisplayNames = null) {
|
||||
this.alertEntryType = alertEntryType;
|
||||
this.createdAt = createdAt;
|
||||
this.detiled = detiled;
|
||||
this.entryType = entryType;
|
||||
this.errorString = errorString;
|
||||
this.methodName = methodName;
|
||||
this.methodSimpleName = methodSimpleName;
|
||||
this.partiallySupportedChange = partiallySupportedChange;
|
||||
this.patchedMembersDisplayNames = patchedMembersDisplayNames;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AlertEntry {
|
||||
internal readonly AlertType alertType;
|
||||
internal readonly string title;
|
||||
internal readonly DateTime timestamp;
|
||||
internal readonly string description;
|
||||
[CanBeNull] internal readonly Action actionData;
|
||||
internal readonly AlertType iconType;
|
||||
internal readonly string shortDescription;
|
||||
internal readonly EntryType entryType;
|
||||
internal readonly AlertData alertData;
|
||||
internal readonly bool hasExitButton;
|
||||
|
||||
internal AlertEntry(AlertType alertType, string title, string description, DateTime timestamp, string shortDescription = null, Action actionData = null, AlertType? iconType = null, EntryType entryType = EntryType.Standalone, AlertData alertData = default(AlertData), bool hasExitButton = true) {
|
||||
this.alertType = alertType;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.shortDescription = shortDescription;
|
||||
this.actionData = actionData;
|
||||
this.iconType = iconType ?? alertType;
|
||||
this.timestamp = timestamp;
|
||||
this.entryType = entryType;
|
||||
this.alertData = alertData;
|
||||
this.hasExitButton = hasExitButton;
|
||||
}
|
||||
}
|
||||
|
||||
internal static class HotReloadTimelineHelper {
|
||||
internal const int maxVisibleEntries = 40;
|
||||
|
||||
private static List<AlertEntry> eventsTimeline = new List<AlertEntry>();
|
||||
internal static List<AlertEntry> EventsTimeline => eventsTimeline;
|
||||
|
||||
static readonly string filePath = Path.Combine(PackageConst.LibraryCachePath, "eventEntries.json");
|
||||
|
||||
public static async Task InitPersistedEvents() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
if (!File.Exists(filePath)) {
|
||||
return;
|
||||
}
|
||||
var redDotShown = HotReloadState.ShowingRedDot;
|
||||
try {
|
||||
var persistedAlertData = await Task.Run(() => JsonConvert.DeserializeObject<PersistedAlertData>(File.ReadAllText(filePath)));
|
||||
eventsTimeline = new List<AlertEntry>(persistedAlertData.alertDatas.Length);
|
||||
for (int i = persistedAlertData.alertDatas.Length - 1; i >= 0; i--) {
|
||||
AlertData alertData = persistedAlertData.alertDatas[i];
|
||||
switch (alertData.alertEntryType) {
|
||||
case AlertEntryType.Error:
|
||||
CreateErrorEventEntry(errorString: alertData.errorString, entryType: alertData.entryType, createdAt: alertData.createdAt);
|
||||
break;
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
case AlertEntryType.InlinedMethod:
|
||||
CreateInlinedMethodsEntry(alertData.patchedMembersDisplayNames, alertData.entryType, alertData.createdAt);
|
||||
break;
|
||||
#endif
|
||||
case AlertEntryType.Failure:
|
||||
if (alertData.entryType == EntryType.Parent) {
|
||||
CreateReloadFinishedWithWarningsEventEntry(createdAt: alertData.createdAt, patchedMembersDisplayNames: alertData.patchedMembersDisplayNames);
|
||||
} else {
|
||||
CreatePatchFailureEventEntry(errorString: alertData.errorString, methodName: alertData.methodName, methodSimpleName: alertData.methodSimpleName, entryType: alertData.entryType, createdAt: alertData.createdAt);
|
||||
}
|
||||
break;
|
||||
case AlertEntryType.PatchApplied:
|
||||
CreateReloadFinishedEventEntry(
|
||||
createdAt: alertData.createdAt,
|
||||
patchedMethodsDisplayNames: alertData.patchedMembersDisplayNames
|
||||
);
|
||||
break;
|
||||
case AlertEntryType.PartiallySupportedChange:
|
||||
if (alertData.entryType == EntryType.Parent) {
|
||||
CreateReloadPartiallyAppliedEventEntry(createdAt: alertData.createdAt, patchedMethodsDisplayNames: alertData.patchedMembersDisplayNames);
|
||||
} else {
|
||||
CreatePartiallyAppliedEventEntry(alertData.partiallySupportedChange, entryType: alertData.entryType, detailed: alertData.detiled, createdAt: alertData.createdAt);
|
||||
}
|
||||
break;
|
||||
case AlertEntryType.UndetectedChange:
|
||||
CreateReloadUndetectedChangeEventEntry(createdAt: alertData.createdAt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.Warning(Translations.Errors.WarningInitializingEventEntries, e);
|
||||
} finally {
|
||||
// Ensure red dot is not triggered for existing entries
|
||||
HotReloadState.ShowingRedDot = redDotShown;
|
||||
}
|
||||
}
|
||||
|
||||
internal static async Task PersistTimeline() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
var persistedData = new PersistedAlertData(eventsTimeline.Where(x => x.alertType != AlertType.CompileError).Select(x => x.alertData).ToArray());
|
||||
try {
|
||||
await Task.Run(() => File.WriteAllText(path: filePath, contents: JsonConvert.SerializeObject(persistedData)));
|
||||
} catch (Exception e) {
|
||||
Log.Warning(Translations.Errors.WarningPersistingEventEntries, e);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearPersistance() {
|
||||
if (MultiplayerPlaymodeHelper.IsClone) {
|
||||
return;
|
||||
}
|
||||
Task.Run(() => File.Delete(filePath));
|
||||
eventsTimeline = new List<AlertEntry>();
|
||||
}
|
||||
|
||||
internal static readonly Dictionary<AlertType, string> alertIconString = new Dictionary<AlertType, string> {
|
||||
{ AlertType.Suggestion, "alert_info" },
|
||||
{ AlertType.UnsupportedChange, "warning" },
|
||||
{ AlertType.CompileError, "error" },
|
||||
{ AlertType.PartiallySupportedChange, "infos" },
|
||||
{ AlertType.AppliedChange, "applied_patch" },
|
||||
{ AlertType.UndetectedChange, "undetected" },
|
||||
};
|
||||
|
||||
#pragma warning disable CS0612 // obsolete
|
||||
public static Dictionary<PartiallySupportedChange, string> partiallySupportedChangeDescriptions => new Dictionary<PartiallySupportedChange, string> {
|
||||
{PartiallySupportedChange.LambdaClosure, Translations.Timeline.PartiallySupportedLambdaClosure},
|
||||
{PartiallySupportedChange.EditAsyncMethod, Translations.Timeline.PartiallySupportedEditAsyncMethod},
|
||||
{PartiallySupportedChange.AddMonobehaviourMethod, Translations.Timeline.PartiallySupportedAddMonobehaviourMethod},
|
||||
{PartiallySupportedChange.EditMonobehaviourField, Translations.Timeline.PartiallySupportedEditMonobehaviourField},
|
||||
{PartiallySupportedChange.EditCoroutine, Translations.Timeline.PartiallySupportedEditCoroutine},
|
||||
{PartiallySupportedChange.EditGenericFieldInitializer, Translations.Timeline.PartiallySupportedEditGenericFieldInitializer},
|
||||
{PartiallySupportedChange.AddEnumMember, Translations.Timeline.PartiallySupportedAddEnumMember},
|
||||
{PartiallySupportedChange.EditFieldInitializer, Translations.Timeline.PartiallySupportedEditFieldInitializer},
|
||||
{PartiallySupportedChange.AddMethodWithAttributes, Translations.Timeline.PartiallySupportedAddMethodWithAttributes},
|
||||
{PartiallySupportedChange.AddFieldWithAttributes, Translations.Timeline.PartiallySupportedAddFieldWithAttributes},
|
||||
{PartiallySupportedChange.GenericMethodInGenericClass, Translations.Timeline.PartiallySupportedGenericMethodInGenericClass},
|
||||
{PartiallySupportedChange.NewCustomSerializableField, Translations.Timeline.PartiallySupportedNewCustomSerializableField},
|
||||
{PartiallySupportedChange.MultipleFieldsEditedInTheSameType, Translations.Timeline.PartiallySupportedMultipleFieldsEditedInTheSameType},
|
||||
};
|
||||
#pragma warning restore CS0612
|
||||
|
||||
internal static List<AlertEntry> Suggestions = new List<AlertEntry>();
|
||||
internal static int UnsupportedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.UnsupportedChange && alert.entryType != EntryType.Child);
|
||||
internal static int PartiallySupportedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.PartiallySupportedChange && alert.entryType != EntryType.Child);
|
||||
internal static int UndetectedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.UndetectedChange && alert.entryType != EntryType.Child);
|
||||
internal static int CompileErrorsCount => EventsTimeline.Count(alert => alert.alertType == AlertType.CompileError);
|
||||
internal static int AppliedChangesCount => EventsTimeline.Count(alert => alert.alertType == AlertType.AppliedChange);
|
||||
|
||||
static Regex shortDescriptionRegex = new Regex(PackageConst.DefaultLocale == Locale.SimplifiedChinese ? @"^([\p{L}\p{N}_]+)\s([\p{L}\p{N}_]+)(?=:)" : @"^(\w+)\s(\w+)(?=:)", RegexOptions.Compiled);
|
||||
|
||||
internal static int GetRunTabTimelineEventCount() {
|
||||
int total = 0;
|
||||
if (HotReloadPrefs.RunTabUnsupportedChangesFilter) {
|
||||
total += UnsupportedChangesCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter) {
|
||||
total += PartiallySupportedChangesCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabUndetectedPatchesFilter) {
|
||||
total += UndetectedChangesCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabCompileErrorFilter) {
|
||||
total += CompileErrorsCount;
|
||||
}
|
||||
if (HotReloadPrefs.RunTabAppliedPatchesFilter) {
|
||||
total += AppliedChangesCount;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
internal static List<AlertEntry> expandedEntries = new List<AlertEntry>();
|
||||
|
||||
internal static void RenderCompileButton() {
|
||||
if (GUILayout.Button(Translations.Common.ButtonRecompile.Trim(), GUILayout.Width(80))) {
|
||||
HotReloadRunTab.RecompileWithChecks();
|
||||
}
|
||||
}
|
||||
|
||||
private static float maxScrollPos;
|
||||
internal static void RenderErrorEventActions(string description, ErrorData errorData) {
|
||||
int maxLen = 2400;
|
||||
string text = errorData.stacktrace;
|
||||
if (text.Length > maxLen) {
|
||||
text = text.Substring(0, maxLen) + "...";
|
||||
}
|
||||
|
||||
GUILayout.TextArea(text, HotReloadWindowStyles.StacktraceTextAreaStyle);
|
||||
|
||||
if (errorData.file || !errorData.stacktrace.Contains("error CS")) {
|
||||
GUILayout.Space(10f);
|
||||
}
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
if (!errorData.stacktrace.Contains("error CS")) {
|
||||
RenderCompileButton();
|
||||
}
|
||||
|
||||
// Link
|
||||
if (errorData.file) {
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button(errorData.linkString, HotReloadWindowStyles.LinkStyle)) {
|
||||
AssetDatabase.OpenAsset(errorData.file, Math.Max(errorData.lineNumber, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Texture2D GetFilterIcon(int count, AlertType alertType) {
|
||||
if (count == 0) {
|
||||
return GUIHelper.ConvertToGrayscale(alertIconString[alertType]);
|
||||
}
|
||||
return GUIHelper.GetLocalIcon(alertIconString[alertType]);
|
||||
}
|
||||
|
||||
internal static void RenderAlertFilters() {
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
var text = AppliedChangesCount > 999 ? "999+" : " " + AppliedChangesCount;
|
||||
|
||||
HotReloadPrefs.RunTabAppliedPatchesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabAppliedPatchesFilter,
|
||||
new GUIContent(text, GetFilterIcon(AppliedChangesCount, AlertType.AppliedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = UndetectedChangesCount > 999 ? "999+" : " " + UndetectedChangesCount;
|
||||
HotReloadPrefs.RunTabUndetectedPatchesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabUndetectedPatchesFilter,
|
||||
new GUIContent(text, GetFilterIcon(UnsupportedChangesCount, AlertType.UndetectedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = PartiallySupportedChangesCount > 999 ? "999+" : " " + PartiallySupportedChangesCount;
|
||||
HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabPartiallyAppliedPatchesFilter,
|
||||
new GUIContent(text, GetFilterIcon(PartiallySupportedChangesCount, AlertType.PartiallySupportedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = UnsupportedChangesCount > 999 ? "999+" : " " + UnsupportedChangesCount;
|
||||
HotReloadPrefs.RunTabUnsupportedChangesFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabUnsupportedChangesFilter,
|
||||
new GUIContent(text, GetFilterIcon(UnsupportedChangesCount, AlertType.UnsupportedChange)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
|
||||
GUILayout.Space(-1f);
|
||||
|
||||
text = CompileErrorsCount > 999 ? "999+" : " " + CompileErrorsCount;
|
||||
HotReloadPrefs.RunTabCompileErrorFilter = GUILayout.Toggle(
|
||||
HotReloadPrefs.RunTabCompileErrorFilter,
|
||||
new GUIContent(text, GetFilterIcon(CompileErrorsCount, AlertType.CompileError)),
|
||||
HotReloadWindowStyles.EventFiltersStyle);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateErrorEventEntry(string errorString, EntryType entryType = EntryType.Standalone, DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var alertType = errorString.Contains("error CS")
|
||||
? AlertType.CompileError
|
||||
: AlertType.UnsupportedChange;
|
||||
var title = errorString.Contains("error CS")
|
||||
? Translations.Utility.CompileError
|
||||
: Translations.Utility.UnsupportedChange;
|
||||
ErrorData errorData = ErrorData.GetErrorData(errorString);
|
||||
var description = errorData.error;
|
||||
string shortDescription = null;
|
||||
if (alertType != AlertType.CompileError) {
|
||||
shortDescription = shortDescriptionRegex.Match(description).Value;
|
||||
}
|
||||
Action actionData = () => RenderErrorEventActions(description, errorData);
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: alertType,
|
||||
title: title,
|
||||
description: description,
|
||||
shortDescription: shortDescription,
|
||||
actionData: actionData,
|
||||
entryType: entryType,
|
||||
alertData: new AlertData(AlertEntryType.Error, createdAt: timestamp, errorString: errorString, entryType: entryType)
|
||||
));
|
||||
}
|
||||
|
||||
#if UNITY_2020_1_OR_NEWER
|
||||
internal static void CreateInlinedMethodsEntry(string[] patchedMethodsDisplayNames, EntryType entryType = EntryType.Standalone, DateTime? createdAt = null) {
|
||||
var truncated = false;
|
||||
if (patchedMethodsDisplayNames?.Length > 25) {
|
||||
patchedMethodsDisplayNames = TruncateList(patchedMethodsDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMethodsDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMethodsDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.UnsupportedChange,
|
||||
title: Translations.Timeline.EventTitleFailedApplyingPatch,
|
||||
description: $"{Translations.Timeline.EventDescriptionInlinedMethods}\n\n• {(truncated ? patchesList + "\n..." : patchesList)}",
|
||||
entryType: EntryType.Parent,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
RenderCompileButton();
|
||||
var suggestion = HotReloadSuggestionsHelper.suggestionMap[HotReloadSuggestionKind.SwitchToDebugModeForInlinedMethods];
|
||||
if (suggestion?.actionData != null) {
|
||||
suggestion.actionData();
|
||||
}
|
||||
}
|
||||
},
|
||||
alertData: new AlertData(AlertEntryType.InlinedMethod, createdAt: timestamp, patchedMembersDisplayNames: patchedMethodsDisplayNames, entryType: EntryType.Parent)
|
||||
);
|
||||
InsertEntry(entry);
|
||||
if (patchedMethodsDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void CreatePatchFailureEventEntry(string errorString, string methodName, string methodSimpleName = null, EntryType entryType = EntryType.Standalone, DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
ErrorData errorData = ErrorData.GetErrorData(errorString);
|
||||
var title = Translations.Timeline.EventTitleFailedApplyingPatch;
|
||||
Action actionData = () => RenderErrorEventActions(errorData.error, errorData);
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.UnsupportedChange,
|
||||
title: title,
|
||||
description: string.Format(Translations.Timeline.EventDescriptionFailedApplyingPatchTapForMore, title, methodName),
|
||||
shortDescription: methodSimpleName,
|
||||
actionData: actionData,
|
||||
entryType: entryType,
|
||||
alertData: new AlertData(AlertEntryType.Failure, createdAt: timestamp, errorString: errorString, methodName: methodName, methodSimpleName: methodSimpleName, entryType: entryType)
|
||||
));
|
||||
}
|
||||
|
||||
public static T[] TruncateList<T>(T[] originalList, int len) {
|
||||
if (originalList.Length <= len) {
|
||||
return originalList;
|
||||
}
|
||||
// Create a new list with a maximum of 25 items
|
||||
T[] truncatedList = new T[len];
|
||||
|
||||
for (int i = 0; i < originalList.Length && i < len; i++) {
|
||||
truncatedList[i] = originalList[i];
|
||||
}
|
||||
|
||||
return truncatedList;
|
||||
}
|
||||
|
||||
internal static void CreateReloadFinishedEventEntry(DateTime? createdAt = null, string[] patchedMethodsDisplayNames = null) {
|
||||
var truncated = false;
|
||||
if (patchedMethodsDisplayNames?.Length > 25) {
|
||||
patchedMethodsDisplayNames = TruncateList(patchedMethodsDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMethodsDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMethodsDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: AlertType.AppliedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Reloaded],
|
||||
description: patchedMethodsDisplayNames?.Length > 0
|
||||
? $"• {(truncated ? patchesList + "\n..." : patchesList)}"
|
||||
: Translations.Timeline.EventDescriptionNoIssuesFound,
|
||||
entryType: patchedMethodsDisplayNames?.Length > 0 ? EntryType.Parent : EntryType.Standalone,
|
||||
alertData: new AlertData(
|
||||
AlertEntryType.PatchApplied,
|
||||
createdAt: timestamp,
|
||||
entryType: EntryType.Standalone,
|
||||
patchedMembersDisplayNames: patchedMethodsDisplayNames)
|
||||
);
|
||||
|
||||
InsertEntry(entry);
|
||||
if (patchedMethodsDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateReloadFinishedWithWarningsEventEntry(DateTime? createdAt = null, string[] patchedMembersDisplayNames = null) {
|
||||
var truncated = false;
|
||||
if (patchedMembersDisplayNames?.Length > 25) {
|
||||
patchedMembersDisplayNames = TruncateList(patchedMembersDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMembersDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMembersDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: AlertType.UnsupportedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Unsupported],
|
||||
description: patchedMembersDisplayNames?.Length > 0 ? $"• {(truncated ? patchesList + "\n...\n\n" + Translations.Timeline.EventDescriptionSeeUnsupportedChangesBelow : patchesList + "\n\n" + Translations.Timeline.EventDescriptionSeeUnsupportedChangesBelow)}" : Translations.Timeline.EventDescriptionSeeDetailedEntriesBelow,
|
||||
entryType: EntryType.Parent,
|
||||
alertData: new AlertData(AlertEntryType.Failure, createdAt: timestamp, entryType: EntryType.Parent, patchedMembersDisplayNames: patchedMembersDisplayNames)
|
||||
);
|
||||
InsertEntry(entry);
|
||||
if (patchedMembersDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateReloadPartiallyAppliedEventEntry(DateTime? createdAt = null, string[] patchedMethodsDisplayNames = null) {
|
||||
var truncated = false;
|
||||
if (patchedMethodsDisplayNames?.Length > 25) {
|
||||
patchedMethodsDisplayNames = TruncateList(patchedMethodsDisplayNames, 25);
|
||||
truncated = true;
|
||||
}
|
||||
var patchesList = patchedMethodsDisplayNames?.Length > 0 ? string.Join("\n• ", patchedMethodsDisplayNames) : "";
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
var entry = new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType: AlertType.PartiallySupportedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.PartiallySupported],
|
||||
description: patchedMethodsDisplayNames?.Length > 0 ? $"• {(truncated ? patchesList + "\n...\n\n" + Translations.Timeline.EventDescriptionSeePartiallyAppliedChangesBelow : patchesList + "\n\n" + Translations.Timeline.EventDescriptionSeePartiallyAppliedChangesBelow)}" : Translations.Timeline.EventDescriptionSeeDetailedEntriesBelow,
|
||||
entryType: EntryType.Parent,
|
||||
alertData: new AlertData(AlertEntryType.PartiallySupportedChange, createdAt: timestamp, entryType: EntryType.Parent, patchedMembersDisplayNames: patchedMethodsDisplayNames)
|
||||
);
|
||||
InsertEntry(entry);
|
||||
if (patchedMethodsDisplayNames?.Length > 0) {
|
||||
expandedEntries.Add(entry);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void CreateReloadUndetectedChangeEventEntry(DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.UndetectedChange,
|
||||
title: EditorIndicationState.IndicationText[EditorIndicationState.IndicationStatus.Undetected],
|
||||
description: Translations.Timeline.EventDescriptionUndetectedChange,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
RenderCompileButton();
|
||||
GUILayout.FlexibleSpace();
|
||||
OpenURLButton.Render(Translations.Suggestions.ButtonDocs, Constants.UndetectedChangesURL);
|
||||
GUILayout.Space(10f);
|
||||
}
|
||||
},
|
||||
entryType: EntryType.Foldout,
|
||||
alertData: new AlertData(AlertEntryType.UndetectedChange, createdAt: timestamp, entryType: EntryType.Parent)
|
||||
));
|
||||
}
|
||||
|
||||
internal static void CreatePartiallyAppliedEventEntry(PartiallySupportedChange partiallySupportedChange, EntryType entryType = EntryType.Standalone, bool detailed = true, DateTime? createdAt = null) {
|
||||
var timestamp = createdAt ?? DateTime.Now;
|
||||
string description;
|
||||
if (!partiallySupportedChangeDescriptions.TryGetValue(partiallySupportedChange, out description)) {
|
||||
return;
|
||||
}
|
||||
InsertEntry(new AlertEntry(
|
||||
timestamp: timestamp,
|
||||
alertType : AlertType.PartiallySupportedChange,
|
||||
title : detailed ? Translations.Timeline.EventTitleChangePartiallyApplied : ToString(partiallySupportedChange),
|
||||
description : description,
|
||||
shortDescription: detailed ? ToString(partiallySupportedChange) : null,
|
||||
actionData: () => {
|
||||
GUILayout.Space(10f);
|
||||
using (new EditorGUILayout.HorizontalScope()) {
|
||||
RenderCompileButton();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GetPartiallySupportedChangePref(partiallySupportedChange)) {
|
||||
if (GUILayout.Button(Translations.Timeline.ButtonIgnoreEventType, HotReloadWindowStyles.LinkStyle)) {
|
||||
HidePartiallySupportedChange(partiallySupportedChange);
|
||||
HotReloadRunTab.RepaintInstant();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
entryType: entryType,
|
||||
alertData: new AlertData(AlertEntryType.PartiallySupportedChange, createdAt: timestamp, partiallySupportedChange: partiallySupportedChange, entryType: entryType, detiled: detailed)
|
||||
));
|
||||
}
|
||||
|
||||
internal static void InsertEntry(AlertEntry entry) {
|
||||
eventsTimeline.Insert(0, entry);
|
||||
if (entry.alertType != AlertType.AppliedChange) {
|
||||
HotReloadState.ShowingRedDot = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void ClearEntries() {
|
||||
eventsTimeline.Clear();
|
||||
}
|
||||
|
||||
internal static bool GetPartiallySupportedChangePref(PartiallySupportedChange key) {
|
||||
return EditorPrefs.GetBool($"HotReloadWindow.ShowPartiallySupportedChangeType.{key}", true);
|
||||
}
|
||||
|
||||
internal static void HidePartiallySupportedChange(PartiallySupportedChange key) {
|
||||
EditorPrefs.SetBool($"HotReloadWindow.ShowPartiallySupportedChangeType.{key}", false);
|
||||
// loop over scroll entries to remove hidden entries
|
||||
for (var i = EventsTimeline.Count - 1; i >= 0; i--) {
|
||||
var eventEntry = EventsTimeline[i];
|
||||
if (eventEntry.alertData.partiallySupportedChange == key) {
|
||||
EventsTimeline.Remove(eventEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// performance optimization (Enum.ToString uses reflection)
|
||||
internal static string ToString(this PartiallySupportedChange change) {
|
||||
#pragma warning disable CS0612 // obsolete
|
||||
switch (change) {
|
||||
case PartiallySupportedChange.LambdaClosure:
|
||||
return nameof(PartiallySupportedChange.LambdaClosure);
|
||||
case PartiallySupportedChange.EditAsyncMethod:
|
||||
return nameof(PartiallySupportedChange.EditAsyncMethod);
|
||||
case PartiallySupportedChange.AddMonobehaviourMethod:
|
||||
return nameof(PartiallySupportedChange.AddMonobehaviourMethod);
|
||||
case PartiallySupportedChange.EditMonobehaviourField:
|
||||
return nameof(PartiallySupportedChange.EditMonobehaviourField);
|
||||
case PartiallySupportedChange.EditCoroutine:
|
||||
return nameof(PartiallySupportedChange.EditCoroutine);
|
||||
case PartiallySupportedChange.EditGenericFieldInitializer:
|
||||
return nameof(PartiallySupportedChange.EditGenericFieldInitializer);
|
||||
case PartiallySupportedChange.AddEnumMember:
|
||||
return nameof(PartiallySupportedChange.AddEnumMember);
|
||||
case PartiallySupportedChange.EditFieldInitializer:
|
||||
return nameof(PartiallySupportedChange.EditFieldInitializer);
|
||||
case PartiallySupportedChange.AddMethodWithAttributes:
|
||||
return nameof(PartiallySupportedChange.AddMethodWithAttributes);
|
||||
case PartiallySupportedChange.GenericMethodInGenericClass:
|
||||
return nameof(PartiallySupportedChange.GenericMethodInGenericClass);
|
||||
case PartiallySupportedChange.AddFieldWithAttributes:
|
||||
return nameof(PartiallySupportedChange.AddFieldWithAttributes);
|
||||
case PartiallySupportedChange.NewCustomSerializableField:
|
||||
return nameof(PartiallySupportedChange.NewCustomSerializableField);
|
||||
case PartiallySupportedChange.MultipleFieldsEditedInTheSameType:
|
||||
return nameof(PartiallySupportedChange.MultipleFieldsEditedInTheSameType);
|
||||
#pragma warning restore CS0612
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(change), change, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user