using System; using UnityEditor; using UnityEngine; using System.Threading.Tasks; using System.Collections.Generic; // All using statements now refer to the new data-only class using static IntelligentProjectAnalyzer.Editor.DependencyBuilderData; namespace IntelligentProjectAnalyzer.Editor { /// /// Acts as the central state machine and orchestrator of the dependency build process. /// This version performs the scan asynchronously and is driven by editor events. /// public static class DependencyStoreBuilder { private const int BatchSize = 20; // Number of assets to process per frame. #region State Management private enum BuilderState { Idle, PreFetching, Analyzing, Finalizing } private static BuilderState _currentState = BuilderState.Idle; // Queues for assets to be processed on the main thread private static Queue _scriptGuidsToProcess; private static Queue _prefabGuidsToProcess; private static Queue _soGuidsToProcess; private static Queue _sceneGuidsToProcess; private static Queue _miscGuidsToProcess; // Lists to store the pre-fetched metadata private static List _allMetadata; private static List _scriptMetadata; private static List _sceneMetadata; private static List _miscMetadata; // Task for background analysis private static Task> _analysisTask; #endregion // Static constructor to hook into the editor update loop. static DependencyStoreBuilder() { EditorApplication.update += OnEditorUpdate; } /// /// Starts the build process. Called by DependencyBuilderTriggers. /// public static void BuildStore() { if (_currentState != BuilderState.Idle) { // A build is already running. return; } // --- Initialize State and Queues --- Debug.Log($"--- Starting Async Dependency Store Build ---"); DependencyCacheManager.CleanAndPrepareCache(); (_scriptGuidsToProcess, _prefabGuidsToProcess, _soGuidsToProcess, _sceneGuidsToProcess, _miscGuidsToProcess) = AssetDataFetcher.FindAssetGuids(); var analyzerSettings = AnalyzerSettingsCrud.GetOrCreateSettings(); if (!analyzerSettings.AnalyzeScripts) { _scriptGuidsToProcess?.Clear(); } if (!analyzerSettings.AnalyzePrefabs) { _prefabGuidsToProcess?.Clear(); } if (!analyzerSettings.AnalyzeScriptableObjects) { _soGuidsToProcess?.Clear(); } if (!analyzerSettings.AnalyzeMiscellaneous) { _miscGuidsToProcess?.Clear(); } _allMetadata = new List(); _scriptMetadata = new List(); _sceneMetadata = new List(); _miscMetadata = new List(); _currentState = BuilderState.PreFetching; } private static void OnEditorUpdate() { // The state machine is processed here, once per editor frame. if (_currentState == BuilderState.Idle) return; try { switch (_currentState) { case BuilderState.PreFetching: ProcessPreFetchingBatch(); break; case BuilderState.Analyzing: WaitForAnalysis(); break; case BuilderState.Finalizing: FinalizeBuild(); break; } } catch (Exception e) { Debug.LogError($"An error occurred during the build process. Aborting. Exception: {e}"); ResetState(); } } private static void ProcessPreFetchingBatch() { var totalAssets = _scriptGuidsToProcess.Count + _prefabGuidsToProcess.Count + _soGuidsToProcess.Count + _sceneGuidsToProcess.Count + _miscGuidsToProcess.Count; var initialTotal = _allMetadata.Count + totalAssets; for (var i = 0; i < BatchSize; i++) { if (_sceneGuidsToProcess.Count > 0) { var meta = AssetDataFetcher.PreFetchSceneMetadata(_sceneGuidsToProcess.Dequeue()); if (meta != null) { _allMetadata.Add(meta); _sceneMetadata.Add(meta); } } else if (_scriptGuidsToProcess.Count > 0) { var meta = AssetDataFetcher.PreFetchScriptMetadata(_scriptGuidsToProcess.Dequeue()); if (meta != null) { _allMetadata.Add(meta); _scriptMetadata.Add(meta); } } else if (_prefabGuidsToProcess.Count > 0) { var meta = AssetDataFetcher.PreFetchPrefabMetadata(_prefabGuidsToProcess.Dequeue()); if (meta != null) _allMetadata.Add(meta); } else if (_soGuidsToProcess.Count > 0) { var meta = AssetDataFetcher.PreFetchScriptableObjectMetadata(_soGuidsToProcess.Dequeue()); if (meta != null) _allMetadata.Add(meta); } else if (_miscGuidsToProcess.Count > 0) { var meta = AssetDataFetcher.PreFetchMiscAssetMetadata(_miscGuidsToProcess.Dequeue()); if (meta != null) { _allMetadata.Add(meta); _miscMetadata.Add(meta); } } else { // All pre-fetching is complete. Debug.Log("Pre-fetching complete. Starting background analysis..."); StartBackgroundAnalysis(); _currentState = BuilderState.Analyzing; return; } } // Update progress bar var progress = initialTotal > 0 ? (float)_allMetadata.Count / initialTotal : 1; EditorUtility.DisplayProgressBar("Building Dependency Store (1/3)", "Pre-fetching asset data...", progress); } private static void StartBackgroundAnalysis() { // Gather all the data needed for the background task. var roslynSetupData = AssetDataFetcher.PreFetchRoslynSetupData(_scriptMetadata); // Start the background task, passing it the data it needs. _analysisTask = Task.Run(() => BackgroundAnalyzer.RunAnalysis(_allMetadata, roslynSetupData)); } private static void WaitForAnalysis() { if (_analysisTask == null) { Debug.LogError("Analysis task was not started correctly. Aborting."); ResetState(); return; } EditorUtility.DisplayProgressBar("Building Dependency Store (2/3)", "Analyzing dependencies in background...", _analysisTask.IsCompleted ? 1f : 0.5f); if (_analysisTask.IsCompleted) { _currentState = BuilderState.Finalizing; } } private static void FinalizeBuild() { EditorUtility.DisplayProgressBar("Building Dependency Store (3/3)", "Writing results to disk...", 0.9f); if (_analysisTask.IsFaulted) { Debug.LogError($"Background analysis failed: {_analysisTask.Exception}"); } else { var results = _analysisTask.Result; DependencyCacheManager.WriteResults(results); Debug.Log("--- Async Dependency Store Build Complete! ---"); } ResetState(); } private static void ResetState() { EditorUtility.ClearProgressBar(); _currentState = BuilderState.Idle; _analysisTask = null; } } }