update project diagnostics in file on sln alteration

This commit is contained in:
Matt Parker
2025-10-18 00:43:16 +10:00
parent 11cb9c1deb
commit 639945007b
7 changed files with 25 additions and 20 deletions

View File

@@ -15,13 +15,10 @@ using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Razor.SemanticTokens;
using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem; using Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Remote.Razor.SemanticTokens; using Microsoft.CodeAnalysis.Remote.Razor.SemanticTokens;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using SharpIDE.Application.Features.Analysis.FixLoaders; using SharpIDE.Application.Features.Analysis.FixLoaders;
using SharpIDE.Application.Features.Analysis.Razor; using SharpIDE.Application.Features.Analysis.Razor;
using SharpIDE.Application.Features.Build; using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.RazorAccess; using SharpIDE.RazorAccess;

View File

@@ -15,6 +15,8 @@ public class GlobalEvents
public EventWrapper<SharpIdeProjectModel, Task> ProjectStoppedRunning { get; } = new(_ => Task.CompletedTask); public EventWrapper<SharpIdeProjectModel, Task> ProjectStoppedRunning { get; } = new(_ => Task.CompletedTask);
public EventWrapper<ExecutionStopInfo, Task> DebuggerExecutionStopped { get; } = new(_ => Task.CompletedTask); public EventWrapper<ExecutionStopInfo, Task> DebuggerExecutionStopped { get; } = new(_ => Task.CompletedTask);
public EventWrapper<SharpIdeFile, Task> IdeFileSavedToDisk { get; } = new(_ => Task.CompletedTask); public EventWrapper<SharpIdeFile, Task> IdeFileSavedToDisk { get; } = new(_ => Task.CompletedTask);
/// A document changed, project was reloaded etc. Document changes include unsaved changes in the IDE.
public EventWrapper<Task> SolutionAltered { get; } = new(() => Task.CompletedTask);
public FileSystemWatcherInternal FileSystemWatcherInternal { get; } = new(); public FileSystemWatcherInternal FileSystemWatcherInternal { get; } = new();
} }

View File

@@ -7,7 +7,7 @@ namespace SharpIDE.Application.Features.FilePersistence;
#pragma warning disable VSTHRD011 #pragma warning disable VSTHRD011
/// Holds the in memory copies of files, and manages saving/loading them to/from disk. /// Holds the in memory copies of files, and manages saving/loading them to/from disk.
public class IdeFileManager public class IdeOpenTabsFileManager
{ {
private ConcurrentDictionary<SharpIdeFile, Lazy<Task<string>>> _openFiles = new(); private ConcurrentDictionary<SharpIdeFile, Lazy<Task<string>>> _openFiles = new();
@@ -35,6 +35,7 @@ public class IdeFileManager
if (file.IsRoslynWorkspaceFile) if (file.IsRoslynWorkspaceFile)
{ {
await RoslynAnalysis.UpdateDocument(file, newText); await RoslynAnalysis.UpdateDocument(file, newText);
GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget();
} }
} }
@@ -49,6 +50,7 @@ public class IdeFileManager
{ {
var text = await textTask; var text = await textTask;
await RoslynAnalysis.UpdateDocument(file, text); await RoslynAnalysis.UpdateDocument(file, text);
GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget();
} }
} }

View File

@@ -53,6 +53,15 @@ public partial class SharpIdeCodeEdit : CodeEdit
SymbolValidate += OnSymbolValidate; SymbolValidate += OnSymbolValidate;
SymbolLookup += OnSymbolLookup; SymbolLookup += OnSymbolLookup;
LinesEditedFrom += OnLinesEditedFrom; 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 public enum LineEditOrigin
@@ -254,7 +263,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
_currentFile.IsDirty.Value = true; _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 await _textChangedCts.CancelAsync(); // Currently the below methods throw, TODO Fix with suppress throwing, and handle
_textChangedCts.Dispose(); _textChangedCts.Dispose();
_textChangedCts = new CancellationTokenSource(); _textChangedCts = new CancellationTokenSource();
@@ -271,11 +280,6 @@ public partial class SharpIdeCodeEdit : CodeEdit
var documentDiagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile, _textChangedCts.Token); var documentDiagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile, _textChangedCts.Token);
await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics)); 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. // 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) foreach (var (affectedFile, updatedText) in affectedFiles)
{ {
await Singletons.FileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText); await Singletons.OpenTabsFileManager.UpdateInMemoryIfOpenAndSaveAsync(affectedFile, updatedText);
affectedFile.FileContentsChangedExternally.InvokeParallelFireAndForget(); affectedFile.FileContentsChangedExternally.InvokeParallelFireAndForget();
} }
}); });
@@ -300,7 +304,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
private async Task OnFileChangedExternallyInMemory() private async Task OnFileChangedExternallyInMemory()
{ {
var fileContents = await Singletons.FileManager.GetFileTextAsync(_currentFile); var fileContents = await Singletons.OpenTabsFileManager.GetFileTextAsync(_currentFile);
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile);
var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile); var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile);
var diagnostics = RoslynAnalysis.GetDocumentDiagnostics(_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 await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); // get off the UI thread
_currentFile = file; _currentFile = file;
var readFileTask = Singletons.FileManager.GetFileTextAsync(file); var readFileTask = Singletons.OpenTabsFileManager.GetFileTextAsync(file);
_currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory); _currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory);
_currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk); _currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk);
@@ -361,7 +365,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
private async Task OnFileChangedExternallyFromDisk() private async Task OnFileChangedExternallyFromDisk()
{ {
await Singletons.FileManager.ReloadFileFromDisk(_currentFile); await Singletons.OpenTabsFileManager.ReloadFileFromDisk(_currentFile);
await OnFileChangedExternallyInMemory(); await OnFileChangedExternallyInMemory();
} }
@@ -428,14 +432,14 @@ public partial class SharpIdeCodeEdit : CodeEdit
{ {
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
await Singletons.FileManager.SaveAllOpenFilesAsync(); await Singletons.OpenTabsFileManager.SaveAllOpenFilesAsync();
}); });
} }
else if (@event.IsActionPressed(InputStringNames.SaveFile)) else if (@event.IsActionPressed(InputStringNames.SaveFile))
{ {
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
await Singletons.FileManager.SaveFileAsync(_currentFile); await Singletons.OpenTabsFileManager.SaveFileAsync(_currentFile);
}); });
} }
} }
@@ -467,7 +471,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
[RequiresGodotUiThread] [RequiresGodotUiThread]
private void SetProjectDiagnostics(ImmutableArray<SharpIdeDiagnostic> diagnostics) private void SetProjectDiagnostics(ImmutableArray<SharpIdeDiagnostic> diagnostics)
{ {
_fileDiagnostics = diagnostics; _projectDiagnosticsForFile = diagnostics;
QueueRedraw(); QueueRedraw();
} }

View File

@@ -49,7 +49,7 @@ public partial class IdeRoot : Control
Singletons.BuildService = BuildService.Instance; Singletons.BuildService = BuildService.Instance;
Singletons.FileWatcher?.Dispose(); Singletons.FileWatcher?.Dispose();
Singletons.FileWatcher = new IdeFileWatcher(); Singletons.FileWatcher = new IdeFileWatcher();
Singletons.FileManager = new IdeFileManager(); Singletons.OpenTabsFileManager = new IdeOpenTabsFileManager();
Singletons.FileExternalChangeHandler = new IdeFileExternalChangeHandler(); Singletons.FileExternalChangeHandler = new IdeFileExternalChangeHandler();
Singletons.FileSavedToDiskHandler = new IdeFileSavedToDiskHandler(); Singletons.FileSavedToDiskHandler = new IdeFileSavedToDiskHandler();
} }

View File

@@ -45,7 +45,7 @@ public partial class IdeWindow : Control
private void OnFocusExited() private void OnFocusExited()
{ {
_ = Task.GodotRun(async () => await Singletons.FileManager.SaveAllOpenFilesAsync()); _ = Task.GodotRun(async () => await Singletons.OpenTabsFileManager.SaveAllOpenFilesAsync());
} }
public void PickSolution(bool fullscreen = false) public void PickSolution(bool fullscreen = false)

View File

@@ -11,7 +11,7 @@ public static class Singletons
public static RunService RunService { get; set; } = null!; public static RunService RunService { get; set; } = null!;
public static BuildService BuildService { get; set; } = null!; public static BuildService BuildService { get; set; } = null!;
public static IdeFileWatcher FileWatcher { 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 IdeFileExternalChangeHandler FileExternalChangeHandler { get; set; } = null!;
public static IdeFileSavedToDiskHandler FileSavedToDiskHandler { get; set; } = null!; public static IdeFileSavedToDiskHandler FileSavedToDiskHandler { get; set; } = null!;
public static AppState AppState { get; set; } = null!; public static AppState AppState { get; set; } = null!;