From 11cb9c1debd205dd7f51ae83946327a5dbaaeafa Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Sat, 18 Oct 2025 00:20:38 +1000 Subject: [PATCH] show project diagnostics in document --- .../Features/Analysis/RoslynAnalysis.cs | 19 +++++++++++ .../Features/CodeEditor/SharpIdeCodeEdit.cs | 32 +++++++++++++++---- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index 900c2d0..62d437d 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -294,6 +294,25 @@ public static class RoslynAnalysis return diagnostics; } + public static async Task> GetProjectDiagnosticsForFile(SharpIdeFile sharpIdeFile) + { + using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnosticsForFile)}"); + await _solutionLoadedTcs.Task; + var cancellationToken = CancellationToken.None; + var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)sharpIdeFile).GetNearestProjectNode()!.FilePath); + var compilation = await project.GetCompilationAsync(cancellationToken); + Guard.Against.Null(compilation, nameof(compilation)); + + var document = await GetDocumentForSharpIdeFile(sharpIdeFile); + + var syntaxTree = compilation.SyntaxTrees.Single(s => s.FilePath == document.FilePath); + var diagnostics = compilation.GetDiagnostics(cancellationToken) + .Where(d => d.Severity is not DiagnosticSeverity.Hidden && d.Location.SourceTree == syntaxTree) + .Select(d => new SharpIdeDiagnostic(syntaxTree.GetMappedLineSpan(d.Location.SourceSpan).Span, d)) + .ToImmutableArray(); + return diagnostics; + } + public static async Task> GetDocumentDiagnostics(SharpIdeFile fileModel, CancellationToken cancellationToken = default) { if (fileModel.IsRoslynWorkspaceFile is false) return []; diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index cd0cfb8..1473c44 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -4,9 +4,11 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; using SharpIDE.Application; using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Debugging; +using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.RazorAccess; @@ -32,7 +34,8 @@ public partial class SharpIdeCodeEdit : CodeEdit private CustomHighlighter _syntaxHighlighter = new(); private PopupMenu _popupMenu = null!; - private ImmutableArray _diagnostics = []; + private ImmutableArray _fileDiagnostics = []; + private ImmutableArray _projectDiagnosticsForFile = []; private ImmutableArray _currentCodeActionsInPopup = []; private bool _fileChangingSuppressBreakpointToggleEvent; @@ -266,7 +269,12 @@ public partial class SharpIdeCodeEdit : CodeEdit _ = Task.GodotRun(async () => { var documentDiagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile, _textChangedCts.Token); - await this.InvokeAsync(() => SetDiagnosticsModel(documentDiagnostics)); + await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics)); + }); + _ = Task.GodotRun(async () => + { + var projectDiagnosticsForFile = await RoslynAnalysis.GetProjectDiagnosticsForFile(_currentFile); + await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnosticsForFile)); }); }); } @@ -305,7 +313,7 @@ public partial class SharpIdeCodeEdit : CodeEdit BeginComplexOperation(); SetText(fileContents); SetSyntaxHighlightingModel(syntaxHighlighting.Result, razorSyntaxHighlighting.Result); - SetDiagnosticsModel(diagnostics.Result); + SetDiagnostics(diagnostics.Result); SetCaretLine(currentCaretPosition.line); SetCaretColumn(currentCaretPosition.col); SetVScroll(vScroll); @@ -336,6 +344,7 @@ public partial class SharpIdeCodeEdit : CodeEdit var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile); var diagnostics = RoslynAnalysis.GetDocumentDiagnostics(_currentFile); + var projectDiagnosticsForFile = RoslynAnalysis.GetProjectDiagnosticsForFile(_currentFile); var setTextTask = this.InvokeAsync(async () => { _fileChangingSuppressBreakpointToggleEvent = true; @@ -345,7 +354,9 @@ public partial class SharpIdeCodeEdit : CodeEdit await Task.WhenAll(syntaxHighlighting, razorSyntaxHighlighting, setTextTask); // Text must be set before setting syntax highlighting await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting)); await diagnostics; - await this.InvokeAsync(async () => SetDiagnosticsModel(await diagnostics)); + await this.InvokeAsync(async () => SetDiagnostics(await diagnostics)); + await projectDiagnosticsForFile; + await this.InvokeAsync(async () => SetProjectDiagnostics(await projectDiagnosticsForFile)); } private async Task OnFileChangedExternallyFromDisk() @@ -391,7 +402,7 @@ public partial class SharpIdeCodeEdit : CodeEdit public override void _Draw() { //UnderlineRange(_currentLine, _selectionStartCol, _selectionEndCol, new Color(1, 0, 0)); - foreach (var sharpIdeDiagnostic in _diagnostics) + foreach (var sharpIdeDiagnostic in _fileDiagnostics.ConcatFast(_projectDiagnosticsForFile)) { var line = sharpIdeDiagnostic.Span.Start.Line; var startCol = sharpIdeDiagnostic.Span.Start.Character; @@ -447,9 +458,16 @@ public partial class SharpIdeCodeEdit : CodeEdit } [RequiresGodotUiThread] - private void SetDiagnosticsModel(ImmutableArray diagnostics) + private void SetDiagnostics(ImmutableArray diagnostics) { - _diagnostics = diagnostics; + _fileDiagnostics = diagnostics; + QueueRedraw(); + } + + [RequiresGodotUiThread] + private void SetProjectDiagnostics(ImmutableArray diagnostics) + { + _fileDiagnostics = diagnostics; QueueRedraw(); }