receive rather than fetch project diagnostics for file
This commit is contained in:
@@ -300,7 +300,7 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
|
|||||||
_logger.LogInformation("RoslynAnalysis: Solution diagnostics updated in {ElapsedMilliseconds}ms", timer.ElapsedMilliseconds);
|
_logger.LogInformation("RoslynAnalysis: Solution diagnostics updated in {ElapsedMilliseconds}ms", timer.ElapsedMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ImmutableArray<Diagnostic>> GetProjectDiagnostics(SharpIdeProjectModel projectModel, CancellationToken cancellationToken = default)
|
public async Task<ImmutableArray<SharpIdeDiagnostic>> GetProjectDiagnostics(SharpIdeProjectModel projectModel, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnostics)}");
|
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnostics)}");
|
||||||
await _solutionLoadedTcs.Task;
|
await _solutionLoadedTcs.Task;
|
||||||
@@ -308,8 +308,14 @@ 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 diagnostics = compilation.GetDiagnostics(cancellationToken)
|
||||||
diagnostics = diagnostics.Where(d => d.Severity is not DiagnosticSeverity.Hidden).ToImmutableArray();
|
.Where(d => d.Severity is not DiagnosticSeverity.Hidden)
|
||||||
|
.Select(d =>
|
||||||
|
{
|
||||||
|
var mappedFileLinePositionSpan = d.Location.SourceTree!.GetMappedLineSpan(d.Location.SourceSpan);
|
||||||
|
return new SharpIdeDiagnostic(mappedFileLinePositionSpan.Span, d, mappedFileLinePositionSpan.Path);
|
||||||
|
})
|
||||||
|
.ToImmutableArray();
|
||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,7 +333,11 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
|
|||||||
var syntaxTree = compilation.SyntaxTrees.Single(s => s.FilePath == document.FilePath);
|
var syntaxTree = compilation.SyntaxTrees.Single(s => s.FilePath == document.FilePath);
|
||||||
var diagnostics = compilation.GetDiagnostics(cancellationToken)
|
var diagnostics = compilation.GetDiagnostics(cancellationToken)
|
||||||
.Where(d => d.Severity is not DiagnosticSeverity.Hidden && d.Location.SourceTree == syntaxTree)
|
.Where(d => d.Severity is not DiagnosticSeverity.Hidden && d.Location.SourceTree == syntaxTree)
|
||||||
.Select(d => new SharpIdeDiagnostic(syntaxTree.GetMappedLineSpan(d.Location.SourceSpan).Span, d))
|
.Select(d =>
|
||||||
|
{
|
||||||
|
var mappedFileLinePositionSpan = d.Location.SourceTree!.GetMappedLineSpan(d.Location.SourceSpan);
|
||||||
|
return new SharpIdeDiagnostic(mappedFileLinePositionSpan.Span, d, mappedFileLinePositionSpan.Path);
|
||||||
|
})
|
||||||
.ToImmutableArray();
|
.ToImmutableArray();
|
||||||
return diagnostics;
|
return diagnostics;
|
||||||
}
|
}
|
||||||
@@ -347,7 +357,13 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
|
|||||||
|
|
||||||
var diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);
|
var diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);
|
||||||
diagnostics = diagnostics.Where(d => d.Severity is not DiagnosticSeverity.Hidden).ToImmutableArray();
|
diagnostics = diagnostics.Where(d => d.Severity is not DiagnosticSeverity.Hidden).ToImmutableArray();
|
||||||
var result = diagnostics.Select(d => new SharpIdeDiagnostic(semanticModel.SyntaxTree.GetMappedLineSpan(d.Location.SourceSpan).Span, d)).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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ using Microsoft.CodeAnalysis.Text;
|
|||||||
|
|
||||||
namespace SharpIDE.Application.Features.Analysis;
|
namespace SharpIDE.Application.Features.Analysis;
|
||||||
|
|
||||||
public readonly record struct SharpIdeDiagnostic(LinePositionSpan Span, Diagnostic Diagnostic);
|
public readonly record struct SharpIdeDiagnostic(LinePositionSpan Span, Diagnostic Diagnostic, string FilePath);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis;
|
||||||
using ObservableCollections;
|
using ObservableCollections;
|
||||||
|
using SharpIDE.Application.Features.Analysis;
|
||||||
using SharpIDE.Application.Features.Evaluation;
|
using SharpIDE.Application.Features.Evaluation;
|
||||||
using Project = Microsoft.Build.Evaluation.Project;
|
using Project = Microsoft.Build.Evaluation.Project;
|
||||||
|
|
||||||
@@ -138,5 +139,5 @@ public class SharpIdeProjectModel : ISharpIdeNode, IExpandableSharpIdeNode, IChi
|
|||||||
public event Func<Task> ProjectStoppedRunning = () => Task.CompletedTask;
|
public event Func<Task> ProjectStoppedRunning = () => Task.CompletedTask;
|
||||||
public void InvokeProjectStoppedRunning() => ProjectStoppedRunning.Invoke();
|
public void InvokeProjectStoppedRunning() => ProjectStoppedRunning.Invoke();
|
||||||
|
|
||||||
public ObservableHashSet<Diagnostic> Diagnostics { get; internal set; } = [];
|
public ObservableHashSet<SharpIdeDiagnostic> Diagnostics { get; internal set; } = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
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;
|
||||||
@@ -7,6 +8,8 @@ using Microsoft.CodeAnalysis.Rename.ConflictEngine;
|
|||||||
using Microsoft.CodeAnalysis.Shared.Extensions;
|
using Microsoft.CodeAnalysis.Shared.Extensions;
|
||||||
using Microsoft.CodeAnalysis.Tags;
|
using Microsoft.CodeAnalysis.Tags;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
|
using ObservableCollections;
|
||||||
|
using R3;
|
||||||
using Roslyn.Utilities;
|
using Roslyn.Utilities;
|
||||||
using SharpIDE.Application;
|
using SharpIDE.Application;
|
||||||
using SharpIDE.Application.Features.Analysis;
|
using SharpIDE.Application.Features.Analysis;
|
||||||
@@ -107,8 +110,6 @@ 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 _roslynAnalysis.GetDocumentDiagnostics(_currentFile, ct);
|
var documentDiagnostics = await _roslynAnalysis.GetDocumentDiagnostics(_currentFile, ct);
|
||||||
await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics));
|
await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics));
|
||||||
var projectDiagnostics = await _roslynAnalysis.GetProjectDiagnosticsForFile(_currentFile, ct);
|
|
||||||
await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnostics));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LineEditOrigin
|
public enum LineEditOrigin
|
||||||
@@ -255,11 +256,20 @@ public partial class SharpIdeCodeEdit : CodeEdit
|
|||||||
var readFileTask = _openTabsFileManager.GetFileTextAsync(file);
|
var readFileTask = _openTabsFileManager.GetFileTextAsync(file);
|
||||||
_currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternally);
|
_currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternally);
|
||||||
_currentFile.FileDeleted.Subscribe(OnFileDeleted);
|
_currentFile.FileDeleted.Subscribe(OnFileDeleted);
|
||||||
|
var project = ((IChildSharpIdeNode)_currentFile).GetNearestProjectNode();
|
||||||
|
if (project is not null)
|
||||||
|
{
|
||||||
|
project.Diagnostics.ObserveChanged()
|
||||||
|
.SubscribeAwait(async (innerEvent, ct) =>
|
||||||
|
{
|
||||||
|
var projectDiagnosticsForFile = project.Diagnostics.Where(s => s.FilePath == _currentFile.Path).ToImmutableArray();
|
||||||
|
await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnosticsForFile));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
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 projectDiagnosticsForFile = _roslynAnalysis.GetProjectDiagnosticsForFile(_currentFile);
|
|
||||||
await readFileTask;
|
await readFileTask;
|
||||||
var setTextTask = this.InvokeAsync(async () =>
|
var setTextTask = this.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
@@ -275,8 +285,6 @@ 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 projectDiagnosticsForFile;
|
|
||||||
await this.InvokeAsync(async () => SetProjectDiagnostics(await projectDiagnosticsForFile));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,14 +79,14 @@ public partial class ProblemsPanel : Control
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CreateDiagnosticTreeItem(Tree tree, TreeItem parent, ViewChangedEvent<Diagnostic, TreeItemContainer> e)
|
private async Task CreateDiagnosticTreeItem(Tree tree, TreeItem parent, ViewChangedEvent<SharpIdeDiagnostic, TreeItemContainer> e)
|
||||||
{
|
{
|
||||||
await this.InvokeAsync(() =>
|
await this.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
var diagItem = tree.CreateItem(parent);
|
var diagItem = tree.CreateItem(parent);
|
||||||
diagItem.SetText(0, e.NewItem.Value.GetMessage());
|
diagItem.SetText(0, e.NewItem.Value.Diagnostic.GetMessage());
|
||||||
diagItem.SetMetadata(0, new RefCountedContainer<Diagnostic>(e.NewItem.Value));
|
diagItem.SetMetadata(0, new RefCountedContainer<SharpIdeDiagnostic>(e.NewItem.Value));
|
||||||
diagItem.SetIcon(0, e.NewItem.Value.Severity switch
|
diagItem.SetIcon(0, e.NewItem.Value.Diagnostic.Severity switch
|
||||||
{
|
{
|
||||||
DiagnosticSeverity.Error => ErrorIcon,
|
DiagnosticSeverity.Error => ErrorIcon,
|
||||||
DiagnosticSeverity.Warning => WarningIcon,
|
DiagnosticSeverity.Warning => WarningIcon,
|
||||||
@@ -107,13 +107,13 @@ public partial class ProblemsPanel : Control
|
|||||||
private void TreeOnItemActivated()
|
private void TreeOnItemActivated()
|
||||||
{
|
{
|
||||||
var selected = _tree.GetSelected();
|
var selected = _tree.GetSelected();
|
||||||
var diagnosticContainer = selected.GetMetadata(0).As<RefCountedContainer<Diagnostic>?>();
|
var diagnosticContainer = selected.GetMetadata(0).As<RefCountedContainer<SharpIdeDiagnostic>?>();
|
||||||
if (diagnosticContainer is null) return;
|
if (diagnosticContainer is null) return;
|
||||||
var diagnostic = diagnosticContainer.Item;
|
var diagnostic = diagnosticContainer.Item;
|
||||||
var parentTreeItem = selected.GetParent();
|
var parentTreeItem = selected.GetParent();
|
||||||
var projectContainer = parentTreeItem.GetMetadata(0).As<RefCountedContainer<SharpIdeProjectModel>?>();
|
var projectContainer = parentTreeItem.GetMetadata(0).As<RefCountedContainer<SharpIdeProjectModel>?>();
|
||||||
if (projectContainer is null) return;
|
if (projectContainer is null) return;
|
||||||
OpenDocumentContainingDiagnostic(diagnostic);
|
OpenDocumentContainingDiagnostic(diagnostic.Diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenDocumentContainingDiagnostic(Diagnostic diagnostic)
|
private void OpenDocumentContainingDiagnostic(Diagnostic diagnostic)
|
||||||
|
|||||||
@@ -11,11 +11,11 @@
|
|||||||
<MudTreeViewItem T="string" TextTypo="Typo.body2" EndTextTypo="Typo.caption" Expanded="false" Icon="@Icons.Material.Filled.Code" IconColor="Color.Success" Value="@ProjectModel.Name" Text="@ProjectModel.Name" EndText="@($"{_diagnostics.Length} diagnostics")">
|
<MudTreeViewItem T="string" TextTypo="Typo.body2" EndTextTypo="Typo.caption" Expanded="false" Icon="@Icons.Material.Filled.Code" IconColor="Color.Success" Value="@ProjectModel.Name" Text="@ProjectModel.Name" EndText="@($"{_diagnostics.Length} diagnostics")">
|
||||||
@foreach (var diagnostic in _diagnostics)
|
@foreach (var diagnostic in _diagnostics)
|
||||||
{
|
{
|
||||||
<MudTreeViewItem T="string" TextTypo="Typo.body2" OnClick="@(async () => await OpenDocumentContainingDiagnostic(diagnostic))" Icon="@Icons.Material.Filled.Warning" IconColor="@GetDiagnosticIconColour(diagnostic)" Value="@diagnostic.ToString()">
|
<MudTreeViewItem T="string" TextTypo="Typo.body2" OnClick="@(async () => await OpenDocumentContainingDiagnostic(diagnostic.Diagnostic))" Icon="@Icons.Material.Filled.Warning" IconColor="@GetDiagnosticIconColour(diagnostic.Diagnostic)" Value="@diagnostic.ToString()">
|
||||||
<BodyContent>
|
<BodyContent>
|
||||||
<MudText Typo="Typo.body2">
|
<MudText Typo="Typo.body2">
|
||||||
@diagnostic.GetMessage()
|
@diagnostic.Diagnostic.GetMessage()
|
||||||
<MudText Typo="Typo.caption" Style="color: var(--mud-palette-gray-dark)">@diagnostic.Id</MudText>
|
<MudText Typo="Typo.caption" Style="color: var(--mud-palette-gray-dark)">@diagnostic.Diagnostic.Id</MudText>
|
||||||
</MudText>
|
</MudText>
|
||||||
</BodyContent>
|
</BodyContent>
|
||||||
</MudTreeViewItem>
|
</MudTreeViewItem>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
[Parameter]
|
[Parameter]
|
||||||
public EventCallback<SharpIdeFile> OnFileSelected { get; set; }
|
public EventCallback<SharpIdeFile> OnFileSelected { get; set; }
|
||||||
|
|
||||||
private ImmutableArray<Diagnostic> _diagnostics = [];
|
private ImmutableArray<SharpIdeDiagnostic> _diagnostics = [];
|
||||||
|
|
||||||
private static Color GetDiagnosticIconColour(Diagnostic diagnostic) => diagnostic.Severity switch
|
private static Color GetDiagnosticIconColour(Diagnostic diagnostic) => diagnostic.Severity switch
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user