show analyzer diagnostics for file

This commit is contained in:
Matt Parker
2025-12-07 16:18:21 +10:00
parent 5b56274387
commit 0ada943c81
2 changed files with 52 additions and 6 deletions

View File

@@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.MSBuild; using Microsoft.CodeAnalysis.MSBuild;
@@ -340,7 +341,8 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
var compilation = await project.GetCompilationAsync(cancellationToken); var compilation = await project.GetCompilationAsync(cancellationToken);
Guard.Against.Null(compilation, nameof(compilation)); 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) .Where(d => d.Severity is not DiagnosticSeverity.Hidden)
.Select(d => .Select(d =>
{ {
@@ -399,6 +401,38 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
return result; return result;
} }
public async Task<ImmutableArray<SharpIdeDiagnostic>> 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<IsolatedAnalyzerFileReference>()
.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<Document> GetDocumentForSharpIdeFile(SharpIdeFile fileModel, CancellationToken cancellationToken = default) private static async Task<Document> GetDocumentForSharpIdeFile(SharpIdeFile fileModel, CancellationToken cancellationToken = default)
{ {
var project = GetProjectForSharpIdeFile(fileModel); var project = GetProjectForSharpIdeFile(fileModel);

View File

@@ -1,11 +1,8 @@
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Collections.Specialized;
using Godot; using Godot;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Rename.ConflictEngine;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Tags; using Microsoft.CodeAnalysis.Tags;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Threading; using Microsoft.CodeAnalysis.Threading;
@@ -24,7 +21,6 @@ using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.Godot.Features.Problems; using SharpIDE.Godot.Features.Problems;
using SharpIDE.Godot.Features.SymbolLookup;
using Task = System.Threading.Tasks.Task; using Task = System.Threading.Tasks.Task;
namespace SharpIDE.Godot.Features.CodeEditor; namespace SharpIDE.Godot.Features.CodeEditor;
@@ -47,6 +43,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
private PopupMenu _popupMenu = null!; private PopupMenu _popupMenu = null!;
private ImmutableArray<SharpIdeDiagnostic> _fileDiagnostics = []; private ImmutableArray<SharpIdeDiagnostic> _fileDiagnostics = [];
private ImmutableArray<SharpIdeDiagnostic> _fileAnalyzerDiagnostics = [];
private ImmutableArray<SharpIdeDiagnostic> _projectDiagnosticsForFile = []; private ImmutableArray<SharpIdeDiagnostic> _projectDiagnosticsForFile = [];
private ImmutableArray<CodeAction> _currentCodeActionsInPopup = []; private ImmutableArray<CodeAction> _currentCodeActionsInPopup = [];
private bool _fileChangingSuppressBreakpointToggleEvent; private bool _fileChangingSuppressBreakpointToggleEvent;
@@ -117,7 +114,12 @@ public partial class SharpIdeCodeEdit : CodeEdit
await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await documentSyntaxHighlighting, await razorSyntaxHighlighting)); await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await documentSyntaxHighlighting, await razorSyntaxHighlighting));
var documentDiagnostics = await documentDiagnosticsTask; var documentDiagnostics = await documentDiagnosticsTask;
if (newCt.IsCancellationRequested) return; if (newCt.IsCancellationRequested) return;
var documentAnalyzerDiagnosticsTask = _roslynAnalysis.GetDocumentAnalyzerDiagnostics(_currentFile, newCt);
await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics)); 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) if (await hasFocus)
{ {
await _roslynAnalysis.UpdateProjectDiagnosticsForFile(_currentFile, newCt); await _roslynAnalysis.UpdateProjectDiagnosticsForFile(_currentFile, newCt);
@@ -289,6 +291,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
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);
var analyzerDiagnostics = _roslynAnalysis.GetDocumentAnalyzerDiagnostics(_currentFile);
await readFileTask; await readFileTask;
var setTextTask = this.InvokeAsync(async () => var setTextTask = this.InvokeAsync(async () =>
{ {
@@ -304,6 +307,8 @@ public partial class SharpIdeCodeEdit : CodeEdit
await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting)); await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting));
await diagnostics; await diagnostics;
await this.InvokeAsync(async () => SetDiagnostics(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() public override void _Draw()
{ {
//UnderlineRange(_currentLine, _selectionStartCol, _selectionEndCol, new Color(1, 0, 0)); //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 line = sharpIdeDiagnostic.Span.Start.Line;
var startCol = sharpIdeDiagnostic.Span.Start.Character; var startCol = sharpIdeDiagnostic.Span.Start.Character;
@@ -476,6 +481,13 @@ public partial class SharpIdeCodeEdit : CodeEdit
QueueRedraw(); QueueRedraw();
} }
[RequiresGodotUiThread]
private void SetAnalyzerDiagnostics(ImmutableArray<SharpIdeDiagnostic> diagnostics)
{
_fileAnalyzerDiagnostics = diagnostics;
QueueRedraw();
}
[RequiresGodotUiThread] [RequiresGodotUiThread]
private void SetProjectDiagnostics(ImmutableArray<SharpIdeDiagnostic> diagnostics) private void SetProjectDiagnostics(ImmutableArray<SharpIdeDiagnostic> diagnostics)
{ {