get code fixes at position

This commit is contained in:
Matt Parker
2025-08-18 19:36:51 +10:00
parent e4e2d95bf8
commit 39b39cddcc
3 changed files with 61 additions and 21 deletions

View File

@@ -162,6 +162,35 @@ public static class RoslynAnalysis
return result;
}
public static async Task<ImmutableArray<CodeAction>> 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<CodeAction> 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<ImmutableArray<(FileLinePositionSpan fileSpan, CodeAction codeAction)>> GetCodeFixesAsync(Diagnostic diagnostic)
{
var cancellationToken = CancellationToken.None;
@@ -175,7 +204,6 @@ public static class RoslynAnalysis
private static async Task<ImmutableArray<CodeAction>> GetCodeFixesAsync(Document document, Diagnostic diagnostic)
{
var cancellationToken = CancellationToken.None;
var position = diagnostic.Location.SourceSpan.Start;
var codeActions = new List<CodeAction>();
var context = new CodeFixContext(
document,
@@ -192,9 +220,16 @@ public static class RoslynAnalysis
await provider.RegisterCodeFixesAsync(context);
}
return codeActions.ToImmutableArray();
}
private static async Task<ImmutableArray<CodeAction>> GetCodeRefactoringsAsync(Document document, TextSpan span)
{
var cancellationToken = CancellationToken.None;
var codeActions = new List<CodeAction>();
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();
}
}

View File

@@ -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)
{

View File

@@ -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);