From 39b39cddccfd906ada20df068ec1c54ff49a6c9b Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Mon, 18 Aug 2025 19:36:51 +1000 Subject: [PATCH] get code fixes at position --- .../Features/Analysis/RoslynAnalysis.cs | 44 ++++++++++++++++--- src/SharpIDE.Godot/IdeRoot.cs | 8 +--- src/SharpIDE.Godot/SharpIdeCodeEdit.cs | 30 ++++++++++--- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index 3faef64..ea04a6f 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -162,6 +162,35 @@ public static class RoslynAnalysis return result; } + public static async Task> GetCodeFixesForDocumentAtPosition(SharpIdeFile fileModel, LinePosition linePosition) + { + 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); + Guard.Against.Null(document, nameof(document)); + var semanticModel = await document.GetSemanticModelAsync(cancellationToken); + Guard.Against.Null(semanticModel, nameof(semanticModel)); + + var diagnostics = semanticModel.GetDiagnostics(); + var sourceText = await document.GetTextAsync(cancellationToken); + var position = sourceText.Lines.GetPosition(linePosition); + var diagnosticsAtPosition = diagnostics + .Where(d => d.Location.IsInSource && d.Location.SourceSpan.Contains(position)) + .ToImmutableArray(); + + ImmutableArray codeActions = []; + foreach (var diagnostic in diagnosticsAtPosition) + { + var actions = await GetCodeFixesAsync(document, diagnostic); + 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)); + return codeActions; + } + public static async Task> GetCodeFixesAsync(Diagnostic diagnostic) { var cancellationToken = CancellationToken.None; @@ -175,7 +204,6 @@ public static class RoslynAnalysis private static async Task> GetCodeFixesAsync(Document document, Diagnostic diagnostic) { var cancellationToken = CancellationToken.None; - var position = diagnostic.Location.SourceSpan.Start; var codeActions = new List(); var context = new CodeFixContext( document, @@ -192,9 +220,16 @@ public static class RoslynAnalysis await provider.RegisterCodeFixesAsync(context); } + return codeActions.ToImmutableArray(); + } + + private static async Task> GetCodeRefactoringsAsync(Document document, TextSpan span) + { + var cancellationToken = CancellationToken.None; + var codeActions = new List(); var refactorContext = new CodeRefactoringContext( document, - diagnostic.Location.SourceSpan, + span, action => codeActions.Add(action), cancellationToken ); @@ -204,11 +239,6 @@ public static class RoslynAnalysis await provider.ComputeRefactoringsAsync(refactorContext).ConfigureAwait(false); } - if (codeActions.Count is not 0) - { - ; - } - return codeActions.ToImmutableArray(); } } diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs index 4709b01..31151a6 100644 --- a/src/SharpIDE.Godot/IdeRoot.cs +++ b/src/SharpIDE.Godot/IdeRoot.cs @@ -35,13 +35,7 @@ public partial class IdeRoot : Control RoslynAnalysis.StartSolutionAnalysis(path); var infraProject = solutionModel.AllProjects.Single(s => s.Name == "Infrastructure"); var diFile = infraProject.Files.Single(s => s.Name == "DependencyInjection.cs"); - var fileContents = await File.ReadAllTextAsync(diFile.Path); - _sharpIdeCodeEdit.SetText(fileContents); - var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(diFile); - _sharpIdeCodeEdit.ProvideSyntaxHighlighting(syntaxHighlighting); - var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(diFile); - _sharpIdeCodeEdit.ProvideDiagnostics(diagnostics); - + await _sharpIdeCodeEdit.SetSharpIdeFile(diFile); } catch (Exception e) { diff --git a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs index 32b4dd4..62ae8dd 100644 --- a/src/SharpIDE.Godot/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/SharpIdeCodeEdit.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using Godot; using Microsoft.Build.Utilities; @@ -8,6 +9,7 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Classification; using Microsoft.CodeAnalysis.Text; using SharpIDE.Application.Features.Analysis; +using SharpIDE.Application.Features.SolutionDiscovery; using Task = System.Threading.Tasks.Task; namespace SharpIDE.Godot; @@ -21,6 +23,8 @@ public partial class SharpIdeCodeEdit : CodeEdit private int _selectionStartCol; private int _selectionEndCol; + private SharpIdeFile _currentFile = null!; + private CustomHighlighter _syntaxHighlighter = new(); private PopupMenu _popupMenu = null!; @@ -44,6 +48,17 @@ public partial class SharpIdeCodeEdit : CodeEdit }; this.SyntaxHighlighter = _syntaxHighlighter; } + + public async Task SetSharpIdeFile(SharpIdeFile file) + { + _currentFile = file; + var fileContents = await File.ReadAllTextAsync(_currentFile.Path); + SetText(fileContents); + var syntaxHighlighting = await RoslynAnalysis.GetDocumentSyntaxHighlighting(_currentFile); + SetSyntaxHighlightingModel(syntaxHighlighting); + var diagnostics = await RoslynAnalysis.GetDocumentDiagnostics(_currentFile); + SetDiagnosticsModel(diagnostics); + } public void UnderlineRange(int line, int caretStartCol, int caretEndCol, Color color, float thickness = 1.5f) { @@ -104,11 +119,12 @@ public partial class SharpIdeCodeEdit : CodeEdit } } - public void ProvideDiagnostics(ImmutableArray<(FileLinePositionSpan fileSpan, Diagnostic diagnostic)> diagnostics) + private void SetDiagnosticsModel(ImmutableArray<(FileLinePositionSpan fileSpan, Diagnostic diagnostic)> diagnostics) { _diagnostics = diagnostics; } - public void ProvideSyntaxHighlighting(IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> classifiedSpans) + + private void SetSyntaxHighlightingModel(IEnumerable<(FileLinePositionSpan fileSpan, ClassifiedSpan classifiedSpan)> classifiedSpans) { _syntaxHighlighter.ClassifiedSpans = classifiedSpans; Callable.From(() => @@ -135,14 +151,14 @@ public partial class SharpIdeCodeEdit : CodeEdit try { var linePos = new LinePosition(caretLine, caretColumn); - var diagnostic = _diagnostics.FirstOrDefault(d => - d.fileSpan.StartLinePosition <= linePos && d.fileSpan.EndLinePosition >= linePos); - if (diagnostic is (_, null)) return; - var codeActions = await RoslynAnalysis.GetCodeFixesAsync(diagnostic.diagnostic); + // var diagnostic = _diagnostics.FirstOrDefault(d => + // d.fileSpan.StartLinePosition <= linePos && d.fileSpan.EndLinePosition >= linePos); + // if (diagnostic is (_, null)) return; + var codeActions = await RoslynAnalysis.GetCodeFixesForDocumentAtPosition(_currentFile, linePos); Callable.From(() => { _popupMenu.Clear(); - foreach (var (index, (fileSpan, codeAction)) in codeActions.Index()) + foreach (var (index, codeAction) in codeActions.Index()) { _popupMenu.AddItem(codeAction.Title, index); //_popupMenu.SetItemMetadata(menuItem, codeAction);