// MIT License - Copyright (c) 2025 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE namespace WallstopStudios.UnityHelpers.Editor.Tools { using System; using UnityEditor; using UnityEditor.Compilation; using UnityEditor.ShortcutManagement; using UnityEngine; /// /// Provides menu and shortcut access to trigger Unity's script recompilation pipeline. /// public static class ManualRecompile { private const string ShortcutId = "Wallstop Studios/Request Script Compilation"; private const string MenuItemPath = "Tools/Wallstop Studios/Unity Helpers/Request Script Compilation"; private const string LogPrefix = "[Unity Helpers]"; private static readonly ImportAssetOptions RefreshOptions = ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport; internal static Func isCompilationPendingEvaluator = () => EditorApplication.isCompiling; internal static Func IsCompilationPendingEvaluator { get => isCompilationPendingEvaluator; set { if (value != null) { isCompilationPendingEvaluator = value; return; } isCompilationPendingEvaluator = () => EditorApplication.isCompiling; } } internal static bool SkipCompilationRequestForTests { get; set; } internal static Action AssetsRefreshedForTests { get; set; } internal static Action CompilationRequestedForTests { get; set; } [MenuItem(MenuItemPath)] public static void RequestFromMenu() { Request(); } [Shortcut(ShortcutId, KeyCode.R, ShortcutModifiers.Alt | ShortcutModifiers.Action)] public static void RequestFromShortcut() { Request(); } private static void Request() { if (IsCompilationPending()) { Debug.Log( $"{LogPrefix} Script compilation already in progress; manual request skipped." ); return; } bool skipCompilation = SkipCompilationRequestForTests; try { AssetDatabase.Refresh(RefreshOptions); AssetsRefreshedForTests?.Invoke(); if (skipCompilation) { Debug.Log( $"{LogPrefix} Asset database refreshed; compilation request skipped (tests)." ); return; } CompilationRequestedForTests?.Invoke(); CompilationPipeline.RequestScriptCompilation(); Debug.Log($"{LogPrefix} Refreshed assets and requested script compilation."); } finally { // Always reset the skip flag to prevent it from persisting across multiple requests // This ensures clean state even if callbacks throw exceptions if (skipCompilation) { SkipCompilationRequestForTests = false; } } } private static bool IsCompilationPending() { // Defensive check: ensure evaluator is never null if (isCompilationPendingEvaluator == null) { Debug.LogWarning( $"{LogPrefix} Compilation pending evaluator is null; resetting to default." ); isCompilationPendingEvaluator = () => EditorApplication.isCompiling; } return isCompilationPendingEvaluator(); } } }