From d2af53fd21d7a00e56c3f20844a7c871b8d90a53 Mon Sep 17 00:00:00 2001 From: Victor Chelaru Date: Sat, 3 Feb 2024 05:47:20 -0700 Subject: [PATCH] Refactoring of FileWatchManager to make it easier to work with. --- FRBDK/Glue/Glue/IO/FileWatchManager.cs | 324 ++++++------------ .../MainAnimationChainPlugin.cs | 2 +- 2 files changed, 109 insertions(+), 217 deletions(-) diff --git a/FRBDK/Glue/Glue/IO/FileWatchManager.cs b/FRBDK/Glue/Glue/IO/FileWatchManager.cs index dbbdb20cb..ed3847f99 100644 --- a/FRBDK/Glue/Glue/IO/FileWatchManager.cs +++ b/FRBDK/Glue/Glue/IO/FileWatchManager.cs @@ -33,26 +33,13 @@ static class FileWatchManager { #region Fields - //static FileSystemWatcher mFileSystemWatcher; - static ChangedFileGroup mChangedProjectFiles; - //static FileSystemWatcher mExternallyBuiltFileWatcher; - - //static ChangeInformation ContainedFilesChangeInfo = new ChangeInformation(); - //static ChangeInformation BehaviorChangeInfo = new ChangeInformation(); - //static ChangeInformation ExternallyBuiltFilesChangeInfo = new ChangeInformation(); - static ChangeInformation BuiltFileChangeInfo = new ChangeInformation(); - - //static List mChangedFiles = new List(); - //static List mChangedBehaviors = new List(); - //static List mChangedExternallyBuiltFiles = new List(); - - //static DateTime mLastChangedFileAdded; - //static DateTime mLastChangedExternallyBuiltFileAdded; - + static ChangedFileGroup filesWaitingToBeFlushed; public static bool PerformFlushing = true; + public static bool IsPrintingDiagnosticOutput = false; + static bool IsFlushing; #endregion @@ -61,159 +48,11 @@ static class FileWatchManager static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1); #endregion - #region Event Methods - - //static void FileChangedEventRaised(object sender, FileSystemEventArgs e) - //{ - // IgnoreReason ignoreReason; - // bool isFileIgnored = FileWatchManager.IsFileIgnored(e.FullPath, out ignoreReason); - - // if (!isFileIgnored) - // { - // ContainedFilesChangeInfo.Add(fileName); - // } - // else if (ignoreReason == IgnoreReason.BuiltFile) - // { - // string fileName = e.FullPath; - - // string standardizedFileName = FileManager.Standardize(fileName).ToLower(); - - // // If it's in the bin folder, we still want to notify the PluginManager in case any - // // plugins are going to react to this - // BuiltFileChangeInfo.Add(standardizedFileName); - // } - //} - - //static void ExternallyBuiltFileChangedEventRaised(object sender, FileSystemEventArgs e) - //{ - // lock (LockObject) - // { - // // Normally we want to ignore files that are outside of the directory tree, - // // but not for externally built files. - // //bool isFileIgnored = FileWatchManager.IsFileIgnored(e.FullPath); - - // //if (!isFileIgnored) - // { - // string fileName = e.FullPath; - - // ExternallyBuiltFilesChangeInfo.Add(fileName); - // } - // } - //} - - #endregion - #region Methods - - private static async Task ReactToChangedFile(FileChange file) - { - bool wasAnythingChanged = false; - - IgnoreReason reason = IgnoreReason.NotIgnored; - bool isIgnored = false; - - isIgnored = IsFileIgnoredBasedOnFileType(file.FilePath, out reason); - - if (!isIgnored) - { - bool handled = await UpdateReactor.UpdateFile(file.FilePath, file.ChangeType); - wasAnythingChanged |= handled; - } - else if (reason == IgnoreReason.BuiltFile) - { - Plugins.PluginManager.ReactToChangedBuiltFile(file.FilePath.FullPath); - wasAnythingChanged = true; - } - - - return wasAnythingChanged; - } - - - public static void FlushAndClearIgnores() - { - Flush(); - mChangedProjectFiles.ClearIgnores(); - } - - /// - /// Loops through all files that have been changed since the last flush, allowing - /// Glue (and plugins) to react to these changed files. - /// - /// - /// The handling of the system event is in ChangedFileGroup. - /// - public static async Task Flush() - { - await semaphoreSlim.WaitAsync(); - if (!IsFlushing && PerformFlushing) - { - IsFlushing = true; - - var filesToFlush = new List(); - - - if(mChangedProjectFiles.CanFlush) - { - filesToFlush.AddRange(mChangedProjectFiles.AllFiles); - mChangedProjectFiles.Clear(); - } - - bool shouldRefreshUnreferencedFiles = false; - - var distinctFiles = - filesToFlush.Distinct().ToArray(); - - foreach (var file in distinctFiles) - { - - var fileCopy = file; - - // The task internally will skip files if they are to be ignored, but projects can have - // *so many* generated files, that putting a check here on generated can eliminate hundreds - // of tasks from being created, improving startup performance - IgnoreReason reason; - bool isIgnored = IsFileIgnoredBasedOnFileType(fileCopy.FilePath, out reason); - - // November 12, 2019 - // Vic asks - why do we only ignore files that are generated here? - //var skip = isIgnored && reason == IgnoreReason.GeneratedCodeFile; - var skip = isIgnored; - - if(!skip) - { - // If individual files changed, we will flush. But if a directory changed, we'll ignore that - // for refreshing unreferenced files. Unreferenced files can change if a file changes or is deleted - // or added, so the specific file will appear in this list. Use that not the directory. - if(!fileCopy.FilePath.IsDirectory) - { - shouldRefreshUnreferencedFiles = true; - } - TaskManager.Self.Add(async () => - { - var didReact = await ReactToChangedFile(fileCopy); - if (didReact) - { - UnreferencedFilesManager.Self.IsRefreshRequested = true; - } - }, - "Reacting to changed file " + fileCopy.FilePath); - } - } - - if (shouldRefreshUnreferencedFiles) - { - UnreferencedFilesManager.Self.RefreshUnreferencedFiles(async: true); - } - IsFlushing = false; - } - semaphoreSlim.Release(); - } - public static void Initialize() { - mChangedProjectFiles = new ChangedFileGroup(); + filesWaitingToBeFlushed = new ChangedFileGroup(); // Files like // the .glux and @@ -234,13 +73,7 @@ public static void Initialize() // next change or not - instead we have to keep track of an int // to mark how many changes Glue should ignore. Dictionary mChangesToIgnore = new Dictionary(); - mChangedProjectFiles.SetIgnoreDictionary(mChangesToIgnore); - - // Oct 11, 2022 - why do we sort? This actually adds a bit to - // the loading process since so many files can change on disk. - // Was this for debugging? I can't figure out why we might want this - // so I'm going to remove this to make Glue load faster: - //mChangedProjectFiles.SortDelegate = CompareFiles; + filesWaitingToBeFlushed.SetIgnoreDictionary(mChangesToIgnore); //mExternallyBuiltFileWatcher = new FileSystemWatcher(); //mExternallyBuiltFileWatcher.Filter = "*.*"; @@ -252,13 +85,14 @@ public static void Initialize() public static void IgnoreNextChangeOnFile(FilePath filePath) => IgnoreNextChangeOnFile(filePath.FullPath); + public static void IgnoreNextChangeOnFile(string file) { // all changed file groups share the same instance, so we only // have to use one of them: #if !UNIT_TESTS - mChangedProjectFiles.IgnoreNextChangeOn(file); + filesWaitingToBeFlushed.IgnoreNextChangeOn(file); //if(FileManager.GetExtension(file) == "csproj") //{ // Plugins.PluginManager.ReceiveOutput($"Ignore {file} {mChangedProjectFiles.NumberOfTimesToIgnore(file)} times"); @@ -283,18 +117,18 @@ public static void UpdateToProjectDirectory() } // Could be null if initialization failed due to XNA not being installed - if (mChangedProjectFiles != null) + if (filesWaitingToBeFlushed != null) { - mChangedProjectFiles.Path = directory; - mChangedProjectFiles.Enabled = true; + filesWaitingToBeFlushed.Path = directory; + filesWaitingToBeFlushed.Enabled = true; } } else { // Could be null if initialization failed due to XNA not being installed - if (mChangedProjectFiles != null) + if (filesWaitingToBeFlushed != null) { - mChangedProjectFiles.Enabled = false; + filesWaitingToBeFlushed.Enabled = false; } } } @@ -307,14 +141,6 @@ private static bool ShouldMoveUpForRoot(string directory) return returnValue; } - //public static void SetExternallyBuiltContentDirectory(string absoluteDirectory) - //{ - - // mExternallyBuiltFileWatcher.Path = absoluteDirectory; - // mExternallyBuiltFileWatcher.EnableRaisingEvents = true; - //} - - private static bool IsFileIgnoredBasedOnFileType(FilePath filePath, out IgnoreReason reason) { bool isIgnored = false; @@ -370,50 +196,116 @@ private static bool IsFileIgnoredBasedOnFileType(FilePath filePath, out IgnoreRe return isIgnored; } - private static bool IsBuiltFile(string fileName) + #endregion + + #region Flush Files + + public static void FlushAndClearIgnores() { - if (!FileManager.IsRelative(fileName)) - { - string projectDirectory = ProjectManager.ProjectRootDirectory; - fileName = FileManager.MakeRelative(fileName, projectDirectory); - } - return fileName.StartsWith("bin/") || fileName.StartsWith("bin\\"); + _ = Flush(); + filesWaitingToBeFlushed.ClearIgnores(); } - private static int CompareFiles(FileChange first, FileChange second) + /// + /// Loops through all files that have been changed since the last flush, allowing + /// Glue (and plugins) to react to these changed files. + /// + /// + /// The handling of the system event is in ChangedFileGroup. + /// + public static async Task Flush() { - int firstValue = GetFileValue(first.FilePath.FullPath); - int secondValue = GetFileValue(second.FilePath.FullPath); - // I think I had this backwards, if the second has a greater value, then it should be positive -// return firstValue - secondValue; - return secondValue - firstValue; + await semaphoreSlim.WaitAsync(); + if (!IsFlushing && PerformFlushing) + { + IsFlushing = true; + + var filesToFlush = new List(); + + + if (filesWaitingToBeFlushed.CanFlush) + { + filesToFlush.AddRange(filesWaitingToBeFlushed.AllFiles); + filesWaitingToBeFlushed.Clear(); + } + + bool shouldRefreshUnreferencedFiles = false; + + var distinctFiles = + filesToFlush.Distinct().ToArray(); + + foreach (var file in distinctFiles) + { + + var fileCopy = file; + + // The task internally will skip files if they are to be ignored, but projects can have + // *so many* generated files, that putting a check here on generated can eliminate hundreds + // of tasks from being created, improving startup performance + IgnoreReason reason; + bool isIgnored = IsFileIgnoredBasedOnFileType(fileCopy.FilePath, out reason); + + // November 12, 2019 + // Vic asks - why do we only ignore files that are generated here? + //var skip = isIgnored && reason == IgnoreReason.GeneratedCodeFile; + var skip = isIgnored; + + if (!skip) + { + // If individual files changed, we will flush. But if a directory changed, we'll ignore that + // for refreshing unreferenced files. Unreferenced files can change if a file changes or is deleted + // or added, so the specific file will appear in this list. Use that not the directory. + if (!fileCopy.FilePath.IsDirectory) + { + shouldRefreshUnreferencedFiles = true; + } + TaskManager.Self.Add(async () => + { + var didReact = await FlushChangedFile(fileCopy); + if (didReact) + { + UnreferencedFilesManager.Self.IsRefreshRequested = true; + } + }, + "Reacting to changed file " + fileCopy.FilePath); + } + } + + if (shouldRefreshUnreferencedFiles) + { + UnreferencedFilesManager.Self.RefreshUnreferencedFiles(async: true); + } + IsFlushing = false; + } + semaphoreSlim.Release(); } - static int GetFileValue(string file) + private static async Task FlushChangedFile(FileChange file) { - // CSProj files first - - string extension = FileManager.GetExtension(file); - if (extension == "csproj" || - extension == "vcproj") - { - return 3; - } - else if (extension == "contentproj") - { - return 2; - } - else if (extension == "glux" || extension == "gluj") + bool wasAnythingChanged = false; + + IgnoreReason reason = IgnoreReason.NotIgnored; + bool isIgnored = false; + + isIgnored = IsFileIgnoredBasedOnFileType(file.FilePath, out reason); + + if (!isIgnored) { - return 1; + bool handled = await UpdateReactor.UpdateFile(file.FilePath, file.ChangeType); + wasAnythingChanged |= handled; } - else + else if (reason == IgnoreReason.BuiltFile) { - return 0; + Plugins.PluginManager.ReactToChangedBuiltFile(file.FilePath.FullPath); + wasAnythingChanged = true; } + + + return wasAnythingChanged; } #endregion + } } diff --git a/FRBDK/Glue/OfficialPlugins/AnimationChainPlugin/MainAnimationChainPlugin.cs b/FRBDK/Glue/OfficialPlugins/AnimationChainPlugin/MainAnimationChainPlugin.cs index 8670dac70..b7961007c 100644 --- a/FRBDK/Glue/OfficialPlugins/AnimationChainPlugin/MainAnimationChainPlugin.cs +++ b/FRBDK/Glue/OfficialPlugins/AnimationChainPlugin/MainAnimationChainPlugin.cs @@ -106,7 +106,7 @@ private void HandleTreeViewItemSelected(ITreeNode selectedTreeNode) private void HandleFileChanged(FilePath filePath, FileChangeType fileChange) { - if (filePath.Extension == "achx") + if (filePath.Extension == "achx" && fileChange == FileChangeType.Modified) { this.RefreshErrors();