go to usage/declaration

This commit is contained in:
Matt Parker
2025-10-26 23:43:10 +10:00
parent e0da4e22b2
commit 1670837d21
8 changed files with 377 additions and 3 deletions

View File

@@ -3,6 +3,8 @@ using Godot;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Rename.ConflictEngine;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using SharpIDE.Application;
@@ -15,6 +17,7 @@ using SharpIDE.Application.Features.Run;
using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
using SharpIDE.Godot.Features.Problems;
using SharpIDE.Godot.Features.SymbolLookup;
using SharpIDE.RazorAccess;
using Task = System.Threading.Tasks.Task;
using Timer = Godot.Timer;
@@ -153,9 +156,85 @@ public partial class SharpIdeCodeEdit : CodeEdit
GD.Print($"Breakpoint {(breakpointAdded ? "added" : "removed")} at line {lineForDebugger}");
}
private void OnSymbolLookup(string symbol, long line, long column)
private readonly PackedScene _symbolUsagePopupScene = ResourceLoader.Load<PackedScene>("uid://dq7ss2ha5rk44");
private void OnSymbolLookup(string symbolString, long line, long column)
{
GD.Print($"Symbol lookup requested: {symbol} at line {line}, column {column}");
GD.Print($"Symbol lookup requested: {symbolString} at line {line}, column {column}");
_ = Task.GodotRun(async () =>
{
var (symbol, linePositionSpan, semanticInfo) = await _roslynAnalysis.LookupSymbolSemanticInfo(_currentFile, new LinePosition((int)line, (int)column));
if (symbol is null) return;
//var locations = symbol.Locations;
if (semanticInfo is null) return;
if (semanticInfo.Value.DeclaredSymbol is not null)
{
GD.Print($"Symbol is declared here: {symbolString}");
// TODO: Lookup references instead
var references = await _roslynAnalysis.FindAllSymbolReferences(semanticInfo.Value.DeclaredSymbol);
if (references.Length is 1)
{
var reference = references[0];
var locations = reference.LocationsArray;
if (locations.Length is 1)
{
// Lets jump to the definition
var referenceLocation = locations[0];
var referenceLineSpan = referenceLocation.Location.GetMappedLineSpan();
var sharpIdeFile = Solution!.AllFiles.SingleOrDefault(f => f.Path == referenceLineSpan.Path);
if (sharpIdeFile is null)
{
GD.Print($"Reference file not found in solution: {referenceLineSpan.Path}");
return;
}
await GodotGlobalEvents.Instance.FileExternallySelected.InvokeParallelAsync(sharpIdeFile, new SharpIdeFileLinePosition(referenceLineSpan.Span.Start.Line, referenceLineSpan.Span.Start.Character));
}
else
{
// Show popup to select which reference to go to
var scene = _symbolUsagePopupScene.Instantiate<SymbolLookupPopup>();
var locationsAndFiles = locations.Select(s =>
{
var lineSpan = s.Location.GetMappedLineSpan();
var file = Solution!.AllFiles.SingleOrDefault(f => f.Path == lineSpan.Path);
return (s, file);
}).Where(t => t.file is not null).ToImmutableArray();
scene.Locations = locations;
scene.LocationsAndFiles = locationsAndFiles!;
scene.Symbol = semanticInfo.Value.DeclaredSymbol;
await this.InvokeAsync(() =>
{
AddChild(scene);
scene.PopupCenteredClamped();
});
}
}
}
else if (semanticInfo.Value.ReferencedSymbols.Length is not 0)
{
var referencedSymbol = semanticInfo.Value.ReferencedSymbols.Single(); // Handle more than one when I run into it
var locations = referencedSymbol.Locations;
if (locations.Length is 1)
{
// Lets jump to the definition
var definitionLocation = locations[0];
var definitionLineSpan = definitionLocation.GetMappedLineSpan();
var sharpIdeFile = Solution!.AllFiles.SingleOrDefault(f => f.Path == definitionLineSpan.Path);
if (sharpIdeFile is null)
{
GD.Print($"Definition file not found in solution: {definitionLineSpan.Path}");
return;
}
await GodotGlobalEvents.Instance.FileExternallySelected.InvokeParallelAsync(sharpIdeFile, new SharpIdeFileLinePosition(definitionLineSpan.Span.Start.Line, definitionLineSpan.Span.Start.Character));
}
else
{
// TODO: Show a popup to select which definition location to go to
}
}
});
}
private void OnSymbolValidate(string symbol)