diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index b9901bc..f304f6a 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.MSBuild; @@ -340,7 +341,8 @@ public class RoslynAnalysis(ILogger logger, BuildService buildSe var compilation = await project.GetCompilationAsync(cancellationToken); Guard.Against.Null(compilation, nameof(compilation)); - var diagnostics = compilation.GetDiagnostics(cancellationToken) + var allDiagnostics = compilation.GetDiagnostics(cancellationToken); + var diagnostics = allDiagnostics .Where(d => d.Severity is not DiagnosticSeverity.Hidden) .Select(d => { @@ -399,6 +401,38 @@ public class RoslynAnalysis(ILogger logger, BuildService buildSe return result; } + public async Task> GetDocumentAnalyzerDiagnostics(SharpIdeFile fileModel, CancellationToken cancellationToken = default) + { + if (fileModel.IsRoslynWorkspaceFile is false) return []; + using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentDiagnostics)}"); + await _solutionLoadedTcs.Task; + if (fileModel.IsRoslynWorkspaceFile is false) return []; + + var document = await GetDocumentForSharpIdeFile(fileModel, cancellationToken); + Guard.Against.Null(document, nameof(document)); + + var semanticModel = await document.GetSemanticModelAsync(cancellationToken); + Guard.Against.Null(semanticModel, nameof(semanticModel)); + + var projectAnalyzers = document.Project.AnalyzerReferences + .OfType() + .SelectMany(r => r.GetAnalyzers(document.Project.Language)) + .ToImmutableArray(); + + var compilationWithAnalyzers = semanticModel.Compilation.WithAnalyzers(projectAnalyzers); + + var diagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(cancellationToken: cancellationToken); + diagnostics = diagnostics.Where(d => d.Severity is not DiagnosticSeverity.Hidden).ToImmutableArray(); + var result = diagnostics + .Select(d => + { + var mappedFileLinePositionSpan = semanticModel.SyntaxTree.GetMappedLineSpan(d.Location.SourceSpan); + return new SharpIdeDiagnostic(mappedFileLinePositionSpan.Span, d, mappedFileLinePositionSpan.Path); + }) + .ToImmutableArray(); + return result; + } + private static async Task GetDocumentForSharpIdeFile(SharpIdeFile fileModel, CancellationToken cancellationToken = default) { var project = GetProjectForSharpIdeFile(fileModel); diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index cd69108..287cb22 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -1,11 +1,8 @@ using System.Collections.Immutable; -using System.Collections.Specialized; using Godot; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Rename.ConflictEngine; -using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Threading; @@ -24,7 +21,6 @@ using SharpIDE.Application.Features.Run; using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Godot.Features.Problems; -using SharpIDE.Godot.Features.SymbolLookup; using Task = System.Threading.Tasks.Task; namespace SharpIDE.Godot.Features.CodeEditor; @@ -47,6 +43,7 @@ public partial class SharpIdeCodeEdit : CodeEdit private PopupMenu _popupMenu = null!; private ImmutableArray _fileDiagnostics = []; + private ImmutableArray _fileAnalyzerDiagnostics = []; private ImmutableArray _projectDiagnosticsForFile = []; private ImmutableArray _currentCodeActionsInPopup = []; private bool _fileChangingSuppressBreakpointToggleEvent; @@ -117,7 +114,12 @@ public partial class SharpIdeCodeEdit : CodeEdit await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await documentSyntaxHighlighting, await razorSyntaxHighlighting)); var documentDiagnostics = await documentDiagnosticsTask; if (newCt.IsCancellationRequested) return; + var documentAnalyzerDiagnosticsTask = _roslynAnalysis.GetDocumentAnalyzerDiagnostics(_currentFile, newCt); await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics)); + var documentAnalyzerDiagnostics = await documentAnalyzerDiagnosticsTask; + if (newCt.IsCancellationRequested) return; + await this.InvokeAsync(() => SetAnalyzerDiagnostics(documentAnalyzerDiagnostics)); + if (newCt.IsCancellationRequested) return; if (await hasFocus) { await _roslynAnalysis.UpdateProjectDiagnosticsForFile(_currentFile, newCt); @@ -289,6 +291,7 @@ public partial class SharpIdeCodeEdit : CodeEdit var syntaxHighlighting = _roslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); var razorSyntaxHighlighting = _roslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile); var diagnostics = _roslynAnalysis.GetDocumentDiagnostics(_currentFile); + var analyzerDiagnostics = _roslynAnalysis.GetDocumentAnalyzerDiagnostics(_currentFile); await readFileTask; var setTextTask = this.InvokeAsync(async () => { @@ -304,6 +307,8 @@ public partial class SharpIdeCodeEdit : CodeEdit await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting)); await diagnostics; await this.InvokeAsync(async () => SetDiagnostics(await diagnostics)); + await analyzerDiagnostics; + await this.InvokeAsync(async () => SetAnalyzerDiagnostics(await analyzerDiagnostics)); }); } @@ -350,7 +355,7 @@ public partial class SharpIdeCodeEdit : CodeEdit public override void _Draw() { //UnderlineRange(_currentLine, _selectionStartCol, _selectionEndCol, new Color(1, 0, 0)); - foreach (var sharpIdeDiagnostic in _fileDiagnostics.ConcatFast(_projectDiagnosticsForFile)) + foreach (var sharpIdeDiagnostic in _fileDiagnostics.Concat(_fileAnalyzerDiagnostics).ConcatFast(_projectDiagnosticsForFile)) { var line = sharpIdeDiagnostic.Span.Start.Line; var startCol = sharpIdeDiagnostic.Span.Start.Character; @@ -476,6 +481,13 @@ public partial class SharpIdeCodeEdit : CodeEdit QueueRedraw(); } + [RequiresGodotUiThread] + private void SetAnalyzerDiagnostics(ImmutableArray diagnostics) + { + _fileAnalyzerDiagnostics = diagnostics; + QueueRedraw(); + } + [RequiresGodotUiThread] private void SetProjectDiagnostics(ImmutableArray diagnostics) {