diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index 62d437d..929d324 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -15,13 +15,10 @@ using Microsoft.CodeAnalysis.MSBuild; using Microsoft.CodeAnalysis.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Remote.Razor.SemanticTokens; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using SharpIDE.Application.Features.Analysis.FixLoaders; using SharpIDE.Application.Features.Analysis.Razor; using SharpIDE.Application.Features.Build; -using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.RazorAccess; diff --git a/src/SharpIDE.Application/Features/Events/GlobalEvents.cs b/src/SharpIDE.Application/Features/Events/GlobalEvents.cs index 295385f..7bef13d 100644 --- a/src/SharpIDE.Application/Features/Events/GlobalEvents.cs +++ b/src/SharpIDE.Application/Features/Events/GlobalEvents.cs @@ -15,6 +15,8 @@ public class GlobalEvents public EventWrapper ProjectStoppedRunning { get; } = new(_ => Task.CompletedTask); public EventWrapper DebuggerExecutionStopped { get; } = new(_ => Task.CompletedTask); public EventWrapper IdeFileSavedToDisk { get; } = new(_ => Task.CompletedTask); + /// A document changed, project was reloaded etc. Document changes include unsaved changes in the IDE. + public EventWrapper SolutionAltered { get; } = new(() => Task.CompletedTask); public FileSystemWatcherInternal FileSystemWatcherInternal { get; } = new(); } diff --git a/src/SharpIDE.Application/Features/FilePersistence/IdeFileManager.cs b/src/SharpIDE.Application/Features/FilePersistence/IdeOpenTabsFileManager.cs similarity index 94% rename from src/SharpIDE.Application/Features/FilePersistence/IdeFileManager.cs rename to src/SharpIDE.Application/Features/FilePersistence/IdeOpenTabsFileManager.cs index 5014920..db9a507 100644 --- a/src/SharpIDE.Application/Features/FilePersistence/IdeFileManager.cs +++ b/src/SharpIDE.Application/Features/FilePersistence/IdeOpenTabsFileManager.cs @@ -7,7 +7,7 @@ namespace SharpIDE.Application.Features.FilePersistence; #pragma warning disable VSTHRD011 /// Holds the in memory copies of files, and manages saving/loading them to/from disk. -public class IdeFileManager +public class IdeOpenTabsFileManager { private ConcurrentDictionary>> _openFiles = new(); @@ -35,6 +35,7 @@ public class IdeFileManager if (file.IsRoslynWorkspaceFile) { await RoslynAnalysis.UpdateDocument(file, newText); + GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); } } @@ -49,6 +50,7 @@ public class IdeFileManager { var text = await textTask; await RoslynAnalysis.UpdateDocument(file, text); + GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); } } diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index 1473c44..670158e 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -53,6 +53,15 @@ public partial class SharpIdeCodeEdit : CodeEdit SymbolValidate += OnSymbolValidate; SymbolLookup += OnSymbolLookup; LinesEditedFrom += OnLinesEditedFrom; + GlobalEvents.Instance.SolutionAltered.Subscribe(OnSolutionAltered); + } + + private async Task OnSolutionAltered() + { + if (_currentFile is null) return; + GD.Print("Solution altered, updating project diagnostics for current file"); + var projectDiagnostics = await RoslynAnalysis.GetProjectDiagnosticsForFile(_currentFile); + await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnostics)); } public enum LineEditOrigin @@ -254,7 +263,7 @@ public partial class SharpIdeCodeEdit : CodeEdit _ = Task.GodotRun(async () => { _currentFile.IsDirty.Value = true; - await Singletons.FileManager.UpdateFileTextInMemory(_currentFile, Text); + await Singletons.OpenTabsFileManager.UpdateFileTextInMemory(_currentFile, Text); await _textChangedCts.CancelAsync(); // Currently the below methods throw, TODO Fix with suppress throwing, and handle _textChangedCts.Dispose(); _textChangedCts = new CancellationTokenSource(); @@ -271,11 +280,6 @@ public partial class SharpIdeCodeEdit : CodeEdit var documentDiagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile, _textChangedCts.Token); await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics)); }); - _ = Task.GodotRun(async () => - { - var projectDiagnosticsForFile = await RoslynAnalysis.GetProjectDiagnosticsForFile(_currentFile); - await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnosticsForFile)); - }); }); } @@ -292,7 +296,7 @@ public partial class SharpIdeCodeEdit : CodeEdit // TODO: This can be more efficient - we can just update in memory and proceed with highlighting etc. Save to disk in background. foreach (var (affectedFile, updatedText) in affectedFiles) { - await Singletons.FileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText); + await Singletons.OpenTabsFileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText); affectedFile.FileContentsChangedExternally.InvokeParallelFireAndForget(); } }); @@ -300,7 +304,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private async Task OnFileChangedExternallyInMemory() { - var fileContents = await Singletons.FileManager.GetFileTextAsync(_currentFile); + var fileContents = await Singletons.OpenTabsFileManager.GetFileTextAsync(_currentFile); var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile); var diagnostics = RoslynAnalysis.GetDocumentDiagnostics(_currentFile); @@ -337,7 +341,7 @@ public partial class SharpIdeCodeEdit : CodeEdit { await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); // get off the UI thread _currentFile = file; - var readFileTask = Singletons.FileManager.GetFileTextAsync(file); + var readFileTask = Singletons.OpenTabsFileManager.GetFileTextAsync(file); _currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory); _currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk); @@ -361,7 +365,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private async Task OnFileChangedExternallyFromDisk() { - await Singletons.FileManager.ReloadFileFromDisk(_currentFile); + await Singletons.OpenTabsFileManager.ReloadFileFromDisk(_currentFile); await OnFileChangedExternallyInMemory(); } @@ -428,14 +432,14 @@ public partial class SharpIdeCodeEdit : CodeEdit { _ = Task.GodotRun(async () => { - await Singletons.FileManager.SaveAllOpenFilesAsync(); + await Singletons.OpenTabsFileManager.SaveAllOpenFilesAsync(); }); } else if (@event.IsActionPressed(InputStringNames.SaveFile)) { _ = Task.GodotRun(async () => { - await Singletons.FileManager.SaveFileAsync(_currentFile); + await Singletons.OpenTabsFileManager.SaveFileAsync(_currentFile); }); } } @@ -467,7 +471,7 @@ public partial class SharpIdeCodeEdit : CodeEdit [RequiresGodotUiThread] private void SetProjectDiagnostics(ImmutableArray diagnostics) { - _fileDiagnostics = diagnostics; + _projectDiagnosticsForFile = diagnostics; QueueRedraw(); } diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs index 51f7e03..6becd22 100644 --- a/src/SharpIDE.Godot/IdeRoot.cs +++ b/src/SharpIDE.Godot/IdeRoot.cs @@ -49,7 +49,7 @@ public partial class IdeRoot : Control Singletons.BuildService = BuildService.Instance; Singletons.FileWatcher?.Dispose(); Singletons.FileWatcher = new IdeFileWatcher(); - Singletons.FileManager = new IdeFileManager(); + Singletons.OpenTabsFileManager = new IdeOpenTabsFileManager(); Singletons.FileExternalChangeHandler = new IdeFileExternalChangeHandler(); Singletons.FileSavedToDiskHandler = new IdeFileSavedToDiskHandler(); } diff --git a/src/SharpIDE.Godot/IdeWindow.cs b/src/SharpIDE.Godot/IdeWindow.cs index 7ead535..6c46378 100644 --- a/src/SharpIDE.Godot/IdeWindow.cs +++ b/src/SharpIDE.Godot/IdeWindow.cs @@ -45,7 +45,7 @@ public partial class IdeWindow : Control private void OnFocusExited() { - _ = Task.GodotRun(async () => await Singletons.FileManager.SaveAllOpenFilesAsync()); + _ = Task.GodotRun(async () => await Singletons.OpenTabsFileManager.SaveAllOpenFilesAsync()); } public void PickSolution(bool fullscreen = false) diff --git a/src/SharpIDE.Godot/Singletons.cs b/src/SharpIDE.Godot/Singletons.cs index 65631ed..4a562a0 100644 --- a/src/SharpIDE.Godot/Singletons.cs +++ b/src/SharpIDE.Godot/Singletons.cs @@ -11,7 +11,7 @@ public static class Singletons public static RunService RunService { get; set; } = null!; public static BuildService BuildService { get; set; } = null!; public static IdeFileWatcher FileWatcher { get; set; } = null!; - public static IdeFileManager FileManager { get; set; } = null!; + public static IdeOpenTabsFileManager OpenTabsFileManager { get; set; } = null!; public static IdeFileExternalChangeHandler FileExternalChangeHandler { get; set; } = null!; public static IdeFileSavedToDiskHandler FileSavedToDiskHandler { get; set; } = null!; public static AppState AppState { get; set; } = null!;