diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index 4ac6d1a..e6e365e 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -293,16 +293,13 @@ public static class RoslynAnalysis return diagnostics; } - public static async Task> GetDocumentDiagnostics(SharpIdeFile fileModel) + public static async Task> GetDocumentDiagnostics(SharpIdeFile fileModel, CancellationToken cancellationToken = default) { + if (fileModel.IsRoslynWorkspaceFile is false) return []; using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentDiagnostics)}"); await _solutionLoadedTcs.Task; - var cancellationToken = CancellationToken.None; - var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath); - var document = project.Documents.SingleOrDefault(s => s.FilePath == fileModel.Path); - if (document is null) return []; - //var document = _workspace!.CurrentSolution.GetDocument(fileModel.Path); + var document = await GetDocumentForSharpIdeFile(fileModel); Guard.Against.Null(document, nameof(document)); var semanticModel = await document.GetSemanticModelAsync(cancellationToken); @@ -314,12 +311,31 @@ public static class RoslynAnalysis return result; } + private static async Task GetDocumentForSharpIdeFile(SharpIdeFile fileModel) + { + var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath); + var document = fileModel.IsCsharpFile ? project.Documents.SingleOrDefault(s => s.FilePath == fileModel.Path) + : await GetRazorSourceGeneratedDocumentInProjectForSharpIdeFile(project, fileModel); + Guard.Against.Null(document, nameof(document)); + return document; + } + + private static async Task GetRazorSourceGeneratedDocumentInProjectForSharpIdeFile(Project project, SharpIdeFile fileModel, CancellationToken cancellationToken = default) + { + var razorDocument = project.AdditionalDocuments.Single(s => s.FilePath == fileModel.Path); + + var razorProjectSnapshot = _snapshotManager!.GetSnapshot(project); + var documentSnapshot = razorProjectSnapshot.GetDocument(razorDocument); + + var generatedDocument = await razorProjectSnapshot.GetRequiredGeneratedDocumentAsync(documentSnapshot, cancellationToken); + return generatedDocument; + } + public record SharpIdeRazorMappedClassifiedSpan(SharpIdeRazorSourceSpan SourceSpanInRazor, string CsharpClassificationType); - public static async Task> GetRazorDocumentSyntaxHighlighting(SharpIdeFile fileModel) + public static async Task> GetRazorDocumentSyntaxHighlighting(SharpIdeFile fileModel, CancellationToken cancellationToken = default) { using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetRazorDocumentSyntaxHighlighting)}"); await _solutionLoadedTcs.Task; - var cancellationToken = CancellationToken.None; var timer = Stopwatch.StartNew(); var sharpIdeProjectModel = ((IChildSharpIdeNode) fileModel).GetNearestProjectNode()!; var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == sharpIdeProjectModel!.FilePath); @@ -423,11 +439,10 @@ public static class RoslynAnalysis return sharpIdeRazorSpans; } - public static async Task> GetDocumentSyntaxHighlighting(SharpIdeFile fileModel) + public static async Task> GetDocumentSyntaxHighlighting(SharpIdeFile fileModel, CancellationToken cancellationToken = default) { using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetDocumentSyntaxHighlighting)}"); await _solutionLoadedTcs.Task; - var cancellationToken = CancellationToken.None; var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath); if (fileModel.Name.EndsWith(".cs", StringComparison.OrdinalIgnoreCase) is false) { @@ -458,17 +473,16 @@ public static class RoslynAnalysis return completions; } - public static async Task> GetCodeFixesForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition) + public static async Task> GetCodeFixesForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition, CancellationToken cancellationToken = default) { using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(GetCodeFixesForDocumentAtPosition)}"); - var cancellationToken = CancellationToken.None; - var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath); - var document = project.Documents.Single(s => s.FilePath == fileModel.Path); + await _solutionLoadedTcs.Task; + var document = await GetDocumentForSharpIdeFile(fileModel); Guard.Against.Null(document, nameof(document)); var semanticModel = await document.GetSemanticModelAsync(cancellationToken); Guard.Against.Null(semanticModel, nameof(semanticModel)); - var diagnostics = semanticModel.GetDiagnostics(); + var diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken); // TODO: pass span var sourceText = await document.GetTextAsync(cancellationToken); var position = sourceText.Lines.GetPosition(linePosition); var diagnosticsAtPosition = diagnostics @@ -478,29 +492,18 @@ public static class RoslynAnalysis ImmutableArray codeActions = []; foreach (var diagnostic in diagnosticsAtPosition) { - var actions = await GetCodeFixesAsync(document, diagnostic); + var actions = await GetCodeFixesAsync(document, diagnostic, cancellationToken); codeActions = codeActions.AddRange(actions); } var linePositionSpan = new LinePositionSpan(linePosition, new LinePosition(linePosition.Line, linePosition.Character + 1)); var selectedSpan = sourceText.Lines.GetTextSpan(linePositionSpan); - codeActions = codeActions.AddRange(await GetCodeRefactoringsAsync(document, selectedSpan)); + codeActions = codeActions.AddRange(await GetCodeRefactoringsAsync(document, selectedSpan, cancellationToken)); return codeActions; } - public static async Task> GetCodeFixesAsync(Diagnostic diagnostic) + private static async Task> GetCodeFixesAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken = default) { - var cancellationToken = CancellationToken.None; - var document = _workspace!.CurrentSolution.GetDocument(diagnostic.Location.SourceTree); - Guard.Against.Null(document, nameof(document)); - var codeActions = await GetCodeFixesAsync(document, diagnostic); - var result = codeActions.Select(action => (diagnostic.Location.SourceTree!.GetMappedLineSpan(diagnostic.Location.SourceSpan), action)) - .ToImmutableArray(); - return result; - } - private static async Task> GetCodeFixesAsync(Document document, Diagnostic diagnostic) - { - var cancellationToken = CancellationToken.None; var codeActions = new List(); var context = new CodeFixContext( document, @@ -520,9 +523,8 @@ public static class RoslynAnalysis return codeActions.ToImmutableArray(); } - private static async Task> GetCodeRefactoringsAsync(Document document, TextSpan span) + private static async Task> GetCodeRefactoringsAsync(Document document, TextSpan span, CancellationToken cancellationToken = default) { - var cancellationToken = CancellationToken.None; var codeActions = new List(); var refactorContext = new CodeRefactoringContext( document, @@ -539,9 +541,8 @@ public static class RoslynAnalysis return codeActions.ToImmutableArray(); } - private static async Task GetCompletionsAsync(Document document, LinePosition linePosition) + private static async Task GetCompletionsAsync(Document document, LinePosition linePosition, CancellationToken cancellationToken = default) { - var cancellationToken = CancellationToken.None; var completionService = CompletionService.GetService(document); if (completionService is null) throw new InvalidOperationException("Completion service is not available for the document.");