From e3b18c56f2797d583a609334348fb9c63c5fc7ee Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Fri, 17 Oct 2025 20:05:04 +1000 Subject: [PATCH] Add IdeFileChangeHandler --- .../Features/Events/GlobalEvents.cs | 3 ++ .../FileWatching/IdeFileChangeHandler.cs | 35 +++++++++++++++++++ .../IdeFileExternalChangeHandler.cs | 19 +++------- .../SolutionDiscovery/SharpIdeFile.cs | 3 +- src/SharpIDE.Godot/IdeRoot.cs | 1 + src/SharpIDE.Godot/Singletons.cs | 1 + 6 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 src/SharpIDE.Application/Features/FileWatching/IdeFileChangeHandler.cs diff --git a/src/SharpIDE.Application/Features/Events/GlobalEvents.cs b/src/SharpIDE.Application/Features/Events/GlobalEvents.cs index de24e19..fece697 100644 --- a/src/SharpIDE.Application/Features/Events/GlobalEvents.cs +++ b/src/SharpIDE.Application/Features/Events/GlobalEvents.cs @@ -1,4 +1,5 @@ using SharpIDE.Application.Features.Debugging; +using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.Events; @@ -13,6 +14,8 @@ public class GlobalEvents public EventWrapper ProjectStartedRunning { get; } = new(_ => Task.CompletedTask); public EventWrapper ProjectStoppedRunning { get; } = new(_ => Task.CompletedTask); public EventWrapper DebuggerExecutionStopped { get; } = new(_ => Task.CompletedTask); + /// Meaning saved/persisted to disk + public EventWrapper IdeFileChanged { get; } = new(_ => Task.CompletedTask); public FileSystemWatcherInternal FileSystemWatcherInternal { get; } = new(); } diff --git a/src/SharpIDE.Application/Features/FileWatching/IdeFileChangeHandler.cs b/src/SharpIDE.Application/Features/FileWatching/IdeFileChangeHandler.cs new file mode 100644 index 0000000..e9d85cb --- /dev/null +++ b/src/SharpIDE.Application/Features/FileWatching/IdeFileChangeHandler.cs @@ -0,0 +1,35 @@ +using SharpIDE.Application.Features.Analysis; +using SharpIDE.Application.Features.Evaluation; +using SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.SolutionDiscovery; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; + +namespace SharpIDE.Application.Features.FileWatching; + +public class IdeFileChangeHandler +{ + public SharpIdeSolutionModel SolutionModel { get; set; } = null!; + + public IdeFileChangeHandler() + { + GlobalEvents.Instance.IdeFileChanged.Subscribe(HandleIdeFileChanged); + } + + private async Task HandleIdeFileChanged(SharpIdeFile file) + { + await file.FileContentsChangedExternallyFromDisk.InvokeParallelAsync(); + if (file.IsCsprojFile) + { + await HandleCsprojChanged(file); + } + } + + private async Task HandleCsprojChanged(SharpIdeFile file) + { + var project = SolutionModel.AllProjects.SingleOrDefault(p => p.FilePath == file.Path); + if (project is null) return; + await ProjectEvaluation.ReloadProject(file.Path); + await RoslynAnalysis.ReloadProject(project); + await RoslynAnalysis.UpdateSolutionDiagnostics(); + } +} diff --git a/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs b/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs index aeec7b6..7a5676e 100644 --- a/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs +++ b/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs @@ -23,24 +23,15 @@ public class IdeFileExternalChangeHandler var now = DateTimeOffset.Now; if (now - sharpIdeFile.LastIdeWriteTime.Value < TimeSpan.FromMilliseconds(300)) { - Console.WriteLine($"IdeFileChangeHandler: Ignored - {filePath}"); + Console.WriteLine($"IdeFileExternalChangeHandler: Ignored - {filePath}"); return; } } - Console.WriteLine($"IdeFileChangeHandler: Changed - {filePath}"); - await sharpIdeFile.FileContentsChangedExternallyFromDisk.InvokeParallelAsync(); - if (sharpIdeFile.IsCsprojFile) + Console.WriteLine($"IdeFileExternalChangeHandler: Changed - {filePath}"); + var file = SolutionModel.AllFiles.SingleOrDefault(f => f.Path == filePath); + if (file is not null) { - await HandleCsprojChanged(filePath); + await GlobalEvents.Instance.IdeFileChanged.InvokeParallelAsync(file); } } - - private async Task HandleCsprojChanged(string filePath) - { - var project = SolutionModel.AllProjects.SingleOrDefault(p => p.FilePath == filePath); - if (project is null) return; - await ProjectEvaluation.ReloadProject(filePath); - await RoslynAnalysis.ReloadProject(project); - await RoslynAnalysis.UpdateSolutionDiagnostics(); - } } diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs index ba843b7..afeb701 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs @@ -12,13 +12,14 @@ public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode public required string Path { get; set; } public required string Name { get; set; } public bool IsRazorFile => Path.EndsWith(".razor", StringComparison.OrdinalIgnoreCase); + public bool IsCsprojFile => Path.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase); public bool IsCshtmlFile => Path.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase); public bool IsCsharpFile => Path.EndsWith(".cs", StringComparison.OrdinalIgnoreCase); public bool IsRoslynWorkspaceFile => IsCsharpFile || IsRazorFile || IsCshtmlFile; public required ReactiveProperty IsDirty { get; init; } public required bool SuppressDiskChangeEvents { get; set; } // probably has concurrency issues public required DateTimeOffset? LastIdeWriteTime { get; set; } - public EventWrapper FileContentsChangedExternallyFromDisk { get; } = new(() => Task.CompletedTask); + public EventWrapper FileContentsChangedExternallyFromDisk { get; } = new(() => Task.CompletedTask); // Refactor to global event - this currently doesn't handle updating un-opened files public EventWrapper FileContentsChangedExternally { get; } = new(() => Task.CompletedTask); [SetsRequiredMembers] diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs index a355c73..e98f596 100644 --- a/src/SharpIDE.Godot/IdeRoot.cs +++ b/src/SharpIDE.Godot/IdeRoot.cs @@ -51,6 +51,7 @@ public partial class IdeRoot : Control Singletons.FileWatcher = new IdeFileWatcher(); Singletons.FileManager = new IdeFileManager(); Singletons.FileExternalChangeHandler = new IdeFileExternalChangeHandler(); + Singletons.FileChangeHandler = new IdeFileChangeHandler(); } public override void _Ready() diff --git a/src/SharpIDE.Godot/Singletons.cs b/src/SharpIDE.Godot/Singletons.cs index acbf811..1d1b7d2 100644 --- a/src/SharpIDE.Godot/Singletons.cs +++ b/src/SharpIDE.Godot/Singletons.cs @@ -13,5 +13,6 @@ public static class Singletons public static IdeFileWatcher FileWatcher { get; set; } = null!; public static IdeFileManager FileManager { get; set; } = null!; public static IdeFileExternalChangeHandler FileExternalChangeHandler { get; set; } = null!; + public static IdeFileChangeHandler FileChangeHandler { get; set; } = null!; public static AppState AppState { get; set; } = null!; } \ No newline at end of file