extract partial

This commit is contained in:
Matt Parker
2025-10-27 18:40:35 +10:00
parent 6f462ad74f
commit c6d3e424fe
2 changed files with 95 additions and 82 deletions

View File

@@ -20,7 +20,6 @@ using SharpIDE.Godot.Features.Problems;
using SharpIDE.Godot.Features.SymbolLookup;
using SharpIDE.RazorAccess;
using Task = System.Threading.Tasks.Task;
using Timer = Godot.Timer;
namespace SharpIDE.Godot.Features.CodeEditor;
@@ -156,87 +155,6 @@ public partial class SharpIdeCodeEdit : CodeEdit
GD.Print($"Breakpoint {(breakpointAdded ? "added" : "removed")} at line {lineForDebugger}");
}
private readonly PackedScene _symbolUsagePopupScene = ResourceLoader.Load<PackedScene>("uid://dq7ss2ha5rk44");
private void OnSymbolLookup(string symbolString, long line, long 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)
{
GD.Print($"Symbol validating: {symbol}");

View File

@@ -0,0 +1,95 @@
using System.Collections.Immutable;
using Godot;
using Microsoft.CodeAnalysis.Text;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Godot.Features.SymbolLookup;
namespace SharpIDE.Godot.Features.CodeEditor;
public partial class SharpIdeCodeEdit
{
private readonly PackedScene _symbolUsagePopupScene = ResourceLoader.Load<PackedScene>("uid://dq7ss2ha5rk44");
private void OnSymbolLookup(string symbolString, long line, long 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
}
}
});
}
}