Make RoslynAnalysis non-static

This commit is contained in:
Matt Parker
2025-10-18 16:31:56 +10:00
parent 69d72307f0
commit 6f662bda54
10 changed files with 59 additions and 43 deletions

View File

@@ -29,17 +29,18 @@ using DiagnosticSeverity = Microsoft.CodeAnalysis.DiagnosticSeverity;
namespace SharpIDE.Application.Features.Analysis; namespace SharpIDE.Application.Features.Analysis;
public static class RoslynAnalysis public class RoslynAnalysis
{ {
public static AdhocWorkspace? _workspace; public static AdhocWorkspace? _workspace;
private static CustomMsBuildProjectLoader? _msBuildProjectLoader; private static CustomMsBuildProjectLoader? _msBuildProjectLoader;
private static RemoteSnapshotManager? _snapshotManager; private static RemoteSnapshotManager? _snapshotManager;
private static RemoteSemanticTokensLegendService? _semanticTokensLegendService; private static RemoteSemanticTokensLegendService? _semanticTokensLegendService;
private static SharpIdeSolutionModel? _sharpIdeSolutionModel;
private static HashSet<CodeFixProvider> _codeFixProviders = []; private static HashSet<CodeFixProvider> _codeFixProviders = [];
private static HashSet<CodeRefactoringProvider> _codeRefactoringProviders = []; private static HashSet<CodeRefactoringProvider> _codeRefactoringProviders = [];
private static TaskCompletionSource _solutionLoadedTcs = null!;
public static void StartSolutionAnalysis(SharpIdeSolutionModel solutionModel) private TaskCompletionSource _solutionLoadedTcs = null!;
private SharpIdeSolutionModel? _sharpIdeSolutionModel;
public void StartSolutionAnalysis(SharpIdeSolutionModel solutionModel)
{ {
_solutionLoadedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _solutionLoadedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
_ = Task.Run(async () => _ = Task.Run(async () =>
@@ -54,7 +55,7 @@ public static class RoslynAnalysis
} }
}); });
} }
public static async Task Analyse(SharpIdeSolutionModel solutionModel, CancellationToken cancellationToken = default) public async Task Analyse(SharpIdeSolutionModel solutionModel, CancellationToken cancellationToken = default)
{ {
Console.WriteLine($"RoslynAnalysis: Loading solution"); Console.WriteLine($"RoslynAnalysis: Loading solution");
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(Analyse)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(Analyse)}");
@@ -150,7 +151,7 @@ public static class RoslynAnalysis
/// Callers should call UpdateSolutionDiagnostics after this /// Callers should call UpdateSolutionDiagnostics after this
/// Ensure that the SharpIdeSolutionModel has been updated before calling this and any subsequent calls /// Ensure that the SharpIdeSolutionModel has been updated before calling this and any subsequent calls
public static async Task ReloadSolution(CancellationToken cancellationToken = default) public async Task ReloadSolution(CancellationToken cancellationToken = default)
{ {
Console.WriteLine($"RoslynAnalysis: Reloading Solution"); Console.WriteLine($"RoslynAnalysis: Reloading Solution");
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ReloadSolution)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ReloadSolution)}");
@@ -177,7 +178,7 @@ public static class RoslynAnalysis
/// Callers should call UpdateSolutionDiagnostics after this /// Callers should call UpdateSolutionDiagnostics after this
/// Ensure that the SharpIdeSolutionModel has been updated before calling this and any subsequent calls /// Ensure that the SharpIdeSolutionModel has been updated before calling this and any subsequent calls
public static async Task ReloadProject(SharpIdeProjectModel projectModel, CancellationToken cancellationToken = default) public async Task ReloadProject(SharpIdeProjectModel projectModel, CancellationToken cancellationToken = default)
{ {
Console.WriteLine($"RoslynAnalysis: Reloading Project {projectModel.FilePath}"); Console.WriteLine($"RoslynAnalysis: Reloading Project {projectModel.FilePath}");
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ReloadSolution)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ReloadSolution)}");
@@ -260,7 +261,7 @@ public static class RoslynAnalysis
}).ToImmutableArray(); }).ToImmutableArray();
} }
public static async Task UpdateSolutionDiagnostics() public async Task UpdateSolutionDiagnostics()
{ {
Console.WriteLine("RoslynAnalysis: Updating solution diagnostics"); Console.WriteLine("RoslynAnalysis: Updating solution diagnostics");
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
@@ -277,7 +278,7 @@ public static class RoslynAnalysis
Console.WriteLine($"RoslynAnalysis: Solution diagnostics updated in {timer.ElapsedMilliseconds}ms"); Console.WriteLine($"RoslynAnalysis: Solution diagnostics updated in {timer.ElapsedMilliseconds}ms");
} }
public static async Task<ImmutableArray<Diagnostic>> GetProjectDiagnostics(SharpIdeProjectModel projectModel) public async Task<ImmutableArray<Diagnostic>> GetProjectDiagnostics(SharpIdeProjectModel projectModel)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnostics)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnostics)}");
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
@@ -291,7 +292,7 @@ public static class RoslynAnalysis
return diagnostics; return diagnostics;
} }
public static async Task<ImmutableArray<SharpIdeDiagnostic>> GetProjectDiagnosticsForFile(SharpIdeFile sharpIdeFile) public async Task<ImmutableArray<SharpIdeDiagnostic>> GetProjectDiagnosticsForFile(SharpIdeFile sharpIdeFile)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnosticsForFile)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetProjectDiagnosticsForFile)}");
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
@@ -310,7 +311,7 @@ public static class RoslynAnalysis
return diagnostics; return diagnostics;
} }
public static async Task<ImmutableArray<SharpIdeDiagnostic>> GetDocumentDiagnostics(SharpIdeFile fileModel, CancellationToken cancellationToken = default) public async Task<ImmutableArray<SharpIdeDiagnostic>> GetDocumentDiagnostics(SharpIdeFile fileModel, CancellationToken cancellationToken = default)
{ {
if (fileModel.IsRoslynWorkspaceFile is false) return []; if (fileModel.IsRoslynWorkspaceFile is false) return [];
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentDiagnostics)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentDiagnostics)}");
@@ -349,7 +350,7 @@ public static class RoslynAnalysis
} }
public record SharpIdeRazorMappedClassifiedSpan(SharpIdeRazorSourceSpan SourceSpanInRazor, string CsharpClassificationType); public record SharpIdeRazorMappedClassifiedSpan(SharpIdeRazorSourceSpan SourceSpanInRazor, string CsharpClassificationType);
public static async Task<ImmutableArray<SharpIdeRazorClassifiedSpan>> GetRazorDocumentSyntaxHighlighting(SharpIdeFile fileModel, CancellationToken cancellationToken = default) public async Task<ImmutableArray<SharpIdeRazorClassifiedSpan>> GetRazorDocumentSyntaxHighlighting(SharpIdeFile fileModel, CancellationToken cancellationToken = default)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetRazorDocumentSyntaxHighlighting)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetRazorDocumentSyntaxHighlighting)}");
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
@@ -457,7 +458,7 @@ public static class RoslynAnalysis
} }
// This is expensive for files that have just been updated, making it suboptimal for real-time highlighting // This is expensive for files that have just been updated, making it suboptimal for real-time highlighting
public static async Task<ImmutableArray<SharpIdeClassifiedSpan>> GetDocumentSyntaxHighlighting(SharpIdeFile fileModel, CancellationToken cancellationToken = default) public async Task<ImmutableArray<SharpIdeClassifiedSpan>> GetDocumentSyntaxHighlighting(SharpIdeFile fileModel, CancellationToken cancellationToken = default)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentSyntaxHighlighting)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentSyntaxHighlighting)}");
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
@@ -479,7 +480,7 @@ public static class RoslynAnalysis
return result; return result;
} }
public static async Task<CompletionList> GetCodeCompletionsForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition) public async Task<CompletionList> GetCodeCompletionsForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetCodeCompletionsForDocumentAtPosition)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetCodeCompletionsForDocumentAtPosition)}");
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
@@ -490,7 +491,7 @@ public static class RoslynAnalysis
return completions; return completions;
} }
public static async Task<ImmutableArray<CodeAction>> GetCodeFixesForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition, CancellationToken cancellationToken = default) public async Task<ImmutableArray<CodeAction>> GetCodeFixesForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition, CancellationToken cancellationToken = default)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetCodeFixesForDocumentAtPosition)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetCodeFixesForDocumentAtPosition)}");
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
@@ -575,9 +576,10 @@ public static class RoslynAnalysis
} }
/// Returns the list of files modified by applying the code action /// Returns the list of files modified by applying the code action
public static async Task<List<(SharpIdeFile File, string UpdatedText)>> ApplyCodeActionAsync(CodeAction codeAction) public async Task<List<(SharpIdeFile File, string UpdatedText)>> ApplyCodeActionAsync(CodeAction codeAction)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ApplyCodeActionAsync)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(ApplyCodeActionAsync)}");
await _solutionLoadedTcs.Task;
var cancellationToken = CancellationToken.None; var cancellationToken = CancellationToken.None;
var operations = await codeAction.GetOperationsAsync(cancellationToken); var operations = await codeAction.GetOperationsAsync(cancellationToken);
var changedDocumentIds = new List<DocumentId>(); var changedDocumentIds = new List<DocumentId>();
@@ -617,7 +619,7 @@ public static class RoslynAnalysis
return changedFilesWithText; return changedFilesWithText;
} }
public static async Task<(ISymbol?, LinePositionSpan?)> LookupSymbol(SharpIdeFile fileModel, LinePosition linePosition) public async Task<(ISymbol?, LinePositionSpan?)> LookupSymbol(SharpIdeFile fileModel, LinePosition linePosition)
{ {
await _solutionLoadedTcs.Task; await _solutionLoadedTcs.Task;
var (symbol, linePositionSpan) = fileModel switch var (symbol, linePositionSpan) = fileModel switch
@@ -699,9 +701,10 @@ public static class RoslynAnalysis
return null; return null;
} }
public static async Task UpdateDocument(SharpIdeFile fileModel, string newContent) public async Task UpdateDocument(SharpIdeFile fileModel, string newContent)
{ {
using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(UpdateDocument)}"); using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(UpdateDocument)}");
await _solutionLoadedTcs.Task;
Guard.Against.Null(fileModel, nameof(fileModel)); Guard.Against.Null(fileModel, nameof(fileModel));
Guard.Against.NullOrEmpty(newContent, nameof(newContent)); Guard.Against.NullOrEmpty(newContent, nameof(newContent));

View File

@@ -7,8 +7,10 @@ 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 IdeOpenTabsFileManager public class IdeOpenTabsFileManager(RoslynAnalysis roslynAnalysis)
{ {
private readonly RoslynAnalysis _roslynAnalysis = roslynAnalysis;
private ConcurrentDictionary<SharpIdeFile, Lazy<Task<string>>> _openFiles = new(); private ConcurrentDictionary<SharpIdeFile, Lazy<Task<string>>> _openFiles = new();
/// Implicitly 'opens' a file if not already open, and returns the text. /// Implicitly 'opens' a file if not already open, and returns the text.
@@ -34,7 +36,7 @@ public class IdeOpenTabsFileManager
// Potentially should be event based? // Potentially should be event based?
if (file.IsRoslynWorkspaceFile) if (file.IsRoslynWorkspaceFile)
{ {
await RoslynAnalysis.UpdateDocument(file, newText); await _roslynAnalysis.UpdateDocument(file, newText);
GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget();
} }
} }

View File

@@ -10,11 +10,13 @@ namespace SharpIDE.Application.Features.FileWatching;
public class IdeFileSavedToDiskHandler public class IdeFileSavedToDiskHandler
{ {
private readonly IdeOpenTabsFileManager _openTabsFileManager; private readonly IdeOpenTabsFileManager _openTabsFileManager;
private readonly RoslynAnalysis _roslynAnalysis;
public SharpIdeSolutionModel SolutionModel { get; set; } = null!; public SharpIdeSolutionModel SolutionModel { get; set; } = null!;
public IdeFileSavedToDiskHandler(IdeOpenTabsFileManager openTabsFileManager) public IdeFileSavedToDiskHandler(IdeOpenTabsFileManager openTabsFileManager, RoslynAnalysis roslynAnalysis)
{ {
_openTabsFileManager = openTabsFileManager; _openTabsFileManager = openTabsFileManager;
_roslynAnalysis = roslynAnalysis;
GlobalEvents.Instance.IdeFileSavedToDisk.Subscribe(HandleIdeFileChanged); GlobalEvents.Instance.IdeFileSavedToDisk.Subscribe(HandleIdeFileChanged);
} }
@@ -35,8 +37,8 @@ public class IdeFileSavedToDiskHandler
var project = SolutionModel.AllProjects.SingleOrDefault(p => p.FilePath == file.Path); var project = SolutionModel.AllProjects.SingleOrDefault(p => p.FilePath == file.Path);
if (project is null) return; if (project is null) return;
await ProjectEvaluation.ReloadProject(file.Path); await ProjectEvaluation.ReloadProject(file.Path);
await RoslynAnalysis.ReloadProject(project); await _roslynAnalysis.ReloadProject(project);
await RoslynAnalysis.UpdateSolutionDiagnostics(); await _roslynAnalysis.UpdateSolutionDiagnostics();
} }
private async Task HandleWorkspaceFileChanged(SharpIdeFile file) private async Task HandleWorkspaceFileChanged(SharpIdeFile file)
@@ -48,9 +50,9 @@ public class IdeFileSavedToDiskHandler
var fileText = wasOpenAndUpdated ? var fileText = wasOpenAndUpdated ?
await _openTabsFileManager.GetFileTextAsync(file) : await _openTabsFileManager.GetFileTextAsync(file) :
await File.ReadAllTextAsync(file.Path); await File.ReadAllTextAsync(file.Path);
await RoslynAnalysis.UpdateDocument(file, fileText); await _roslynAnalysis.UpdateDocument(file, fileText);
GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget();
} }
await RoslynAnalysis.UpdateSolutionDiagnostics(); await _roslynAnalysis.UpdateSolutionDiagnostics();
} }
} }

View File

@@ -1,6 +1,7 @@
using Godot; using Godot;
using System.Reflection; using System.Reflection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Build; using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.FilePersistence; using SharpIDE.Application.Features.FilePersistence;
using SharpIDE.Application.Features.FileWatching; using SharpIDE.Application.Features.FileWatching;
@@ -27,6 +28,7 @@ public partial class DiAutoload : Node
services.AddScoped<IdeFileSavedToDiskHandler>(); services.AddScoped<IdeFileSavedToDiskHandler>();
services.AddScoped<IdeFileWatcher>(); services.AddScoped<IdeFileWatcher>();
services.AddScoped<IdeOpenTabsFileManager>(); services.AddScoped<IdeOpenTabsFileManager>();
services.AddScoped<RoslynAnalysis>();
_serviceProvider = services.BuildServiceProvider(); _serviceProvider = services.BuildServiceProvider();
GetTree().NodeAdded += OnNodeAdded; GetTree().NodeAdded += OnNodeAdded;

View File

@@ -43,7 +43,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
[Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!; [Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!;
[Inject] private readonly RunService _runService = null!; [Inject] private readonly RunService _runService = null!;
[Inject] private readonly RoslynAnalysis _roslynAnalysis = null!;
public override void _Ready() public override void _Ready()
{ {
@@ -66,9 +66,9 @@ public partial class SharpIdeCodeEdit : CodeEdit
{ {
if (_currentFile is null) return; if (_currentFile is null) return;
GD.Print("Solution altered, updating project diagnostics for current file"); GD.Print("Solution altered, updating project diagnostics for current file");
var documentDiagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile); var documentDiagnostics = await _roslynAnalysis.GetDocumentDiagnostics(_currentFile);
await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics)); await this.InvokeAsync(() => SetDiagnostics(documentDiagnostics));
var projectDiagnostics = await RoslynAnalysis.GetProjectDiagnosticsForFile(_currentFile); var projectDiagnostics = await _roslynAnalysis.GetProjectDiagnosticsForFile(_currentFile);
await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnostics)); await this.InvokeAsync(() => SetProjectDiagnostics(projectDiagnostics));
} }
@@ -153,7 +153,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
var lineHeight = GetLineHeight(); var lineHeight = GetLineHeight();
GD.Print($"Symbol hovered: {symbol} at line {line}, column {column}"); GD.Print($"Symbol hovered: {symbol} at line {line}, column {column}");
var (roslynSymbol, linePositionSpan) = await RoslynAnalysis.LookupSymbol(_currentFile, new LinePosition((int)line, (int)column)); var (roslynSymbol, linePositionSpan) = await _roslynAnalysis.LookupSymbol(_currentFile, new LinePosition((int)line, (int)column));
if (roslynSymbol is null || linePositionSpan is null) if (roslynSymbol is null || linePositionSpan is null)
{ {
return; return;
@@ -277,15 +277,15 @@ public partial class SharpIdeCodeEdit : CodeEdit
_textChangedCts = new CancellationTokenSource(); _textChangedCts = new CancellationTokenSource();
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
var syntaxHighlighting = RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile, _textChangedCts.Token); var syntaxHighlighting = _roslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile, _textChangedCts.Token);
var razorSyntaxHighlighting = RoslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile, _textChangedCts.Token); var razorSyntaxHighlighting = _roslynAnalysis.GetRazorDocumentSyntaxHighlighting(_currentFile, _textChangedCts.Token);
await Task.WhenAll(syntaxHighlighting, razorSyntaxHighlighting); await Task.WhenAll(syntaxHighlighting, razorSyntaxHighlighting);
await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting)); await this.InvokeAsync(async () => SetSyntaxHighlightingModel(await syntaxHighlighting, await razorSyntaxHighlighting));
__?.Dispose(); __?.Dispose();
}); });
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
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));
}); });
}); });
@@ -300,7 +300,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
var affectedFiles = await RoslynAnalysis.ApplyCodeActionAsync(codeAction); var affectedFiles = await _roslynAnalysis.ApplyCodeActionAsync(codeAction);
// 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)
{ {
@@ -313,9 +313,9 @@ public partial class SharpIdeCodeEdit : CodeEdit
private async Task OnFileChangedExternallyInMemory() private async Task OnFileChangedExternallyInMemory()
{ {
var fileContents = await _openTabsFileManager.GetFileTextAsync(_currentFile); var fileContents = await _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);
await Task.WhenAll(syntaxHighlighting, razorSyntaxHighlighting, diagnostics); await Task.WhenAll(syntaxHighlighting, razorSyntaxHighlighting, diagnostics);
Callable.From(() => Callable.From(() =>
{ {
@@ -351,10 +351,10 @@ public partial class SharpIdeCodeEdit : CodeEdit
_currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory); _currentFile.FileContentsChangedExternally.Subscribe(OnFileChangedExternallyInMemory);
_currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk); _currentFile.FileContentsChangedExternallyFromDisk.Subscribe(OnFileChangedExternallyFromDisk);
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); var projectDiagnosticsForFile = _roslynAnalysis.GetProjectDiagnosticsForFile(_currentFile);
var setTextTask = this.InvokeAsync(async () => var setTextTask = this.InvokeAsync(async () =>
{ {
_fileChangingSuppressBreakpointToggleEvent = true; _fileChangingSuppressBreakpointToggleEvent = true;
@@ -503,7 +503,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
_ = Task.GodotRun(async () => _ = Task.GodotRun(async () =>
{ {
var linePos = new LinePosition(caretLine, caretColumn); var linePos = new LinePosition(caretLine, caretColumn);
var codeActions = await RoslynAnalysis.GetCodeFixesForDocumentAtPosition(_currentFile, linePos); var codeActions = await _roslynAnalysis.GetCodeFixesForDocumentAtPosition(_currentFile, linePos);
await this.InvokeAsync(() => await this.InvokeAsync(() =>
{ {
_popupMenu.Clear(); _popupMenu.Clear();
@@ -529,7 +529,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
{ {
var linePos = new LinePosition(caretLine, caretColumn); var linePos = new LinePosition(caretLine, caretColumn);
var completions = await RoslynAnalysis.GetCodeCompletionsForDocumentAtPosition(_currentFile, linePos); var completions = await _roslynAnalysis.GetCodeCompletionsForDocumentAtPosition(_currentFile, linePos);
await this.InvokeAsync(() => await this.InvokeAsync(() =>
{ {
foreach (var completionItem in completions.ItemsList) foreach (var completionItem in completions.ItemsList)

View File

@@ -21,6 +21,7 @@ file enum ProjectContextMenuOptions
public partial class SolutionExplorerPanel public partial class SolutionExplorerPanel
{ {
private Texture2D _runIcon = ResourceLoader.Load<Texture2D>("uid://bkty6563cthj8"); private Texture2D _runIcon = ResourceLoader.Load<Texture2D>("uid://bkty6563cthj8");
[Inject] private readonly BuildService _buildService = null!; [Inject] private readonly BuildService _buildService = null!;
[Inject] private readonly RunService _runService = null!; [Inject] private readonly RunService _runService = null!;

View File

@@ -43,6 +43,7 @@ public partial class IdeRoot : Control
[Inject] private readonly IdeFileWatcher _fileWatcher = null!; [Inject] private readonly IdeFileWatcher _fileWatcher = null!;
[Inject] private readonly BuildService _buildService = null!; [Inject] private readonly BuildService _buildService = null!;
[Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!; [Inject] private readonly IdeOpenTabsFileManager _openTabsFileManager = null!;
[Inject] private readonly RoslynAnalysis _roslynAnalysis = null!;
public override void _EnterTree() public override void _EnterTree()
{ {
@@ -141,7 +142,7 @@ public partial class IdeRoot : Control
_fileExternalChangeHandler.SolutionModel = solutionModel; _fileExternalChangeHandler.SolutionModel = solutionModel;
_savedToDiskHandler.SolutionModel = solutionModel; _savedToDiskHandler.SolutionModel = solutionModel;
Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred(); Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred();
RoslynAnalysis.StartSolutionAnalysis(solutionModel); _roslynAnalysis.StartSolutionAnalysis(solutionModel);
_fileWatcher.StartWatching(solutionModel); _fileWatcher.StartWatching(solutionModel);
var infraProject = solutionModel.AllProjects.SingleOrDefault(s => s.Name == "WebUi"); var infraProject = solutionModel.AllProjects.SingleOrDefault(s => s.Name == "WebUi");

View File

@@ -4,6 +4,8 @@
@using SharpIDE.Application.Features.SolutionDiscovery @using SharpIDE.Application.Features.SolutionDiscovery
@using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence @using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence
@inject RoslynAnalysis RoslynAnalysis
@if (_diagnostics.Length is not 0) @if (_diagnostics.Length is not 0)
{ {
<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")">

View File

@@ -9,6 +9,7 @@
@inject IDialogService DialogService @inject IDialogService DialogService
@inject BuildService BuildService @inject BuildService BuildService
@inject AppState AppState @inject AppState AppState
@inject RoslynAnalysis RoslynAnalysis
<MudLayout Style="height: 100%"> <MudLayout Style="height: 100%">
<MudAppBar Dense="true" Gutters="false" Class="px-2"> <MudAppBar Dense="true" Gutters="false" Class="px-2">

View File

@@ -3,6 +3,7 @@ using Microsoft.Build.Locator;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using MudBlazor.Services; using MudBlazor.Services;
using Photino.Blazor; using Photino.Blazor;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Build; using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.Run; using SharpIDE.Application.Features.Run;
using SharpIDE.Photino.Models; using SharpIDE.Photino.Models;
@@ -23,6 +24,7 @@ public class Program
appBuilder.Services.AddSingleton<AppState>(); appBuilder.Services.AddSingleton<AppState>();
appBuilder.Services.AddSingleton<BuildService>(); appBuilder.Services.AddSingleton<BuildService>();
appBuilder.Services.AddSingleton<RunService>(); appBuilder.Services.AddSingleton<RunService>();
appBuilder.Services.AddSingleton<RoslynAnalysis>();
appBuilder.RootComponents.Add<App>("app"); appBuilder.RootComponents.Add<App>("app");