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("uid://dq7ss2ha5rk44"); private void OnSymbolLookup(string symbolString, long line, long column) { GD.Print($"Symbol lookup requested: {symbolString} at line {line}, column {column}"); var globalMousePosition = GetGlobalMousePosition(); // don't breakpoint before this, else your mouse position will be wrong var clickedCharRect = GetRectAtLineColumn((int)line, (int)column); var globalPosition = GetGlobalPosition(); var startSymbolCharGlobalPos = clickedCharRect.Position + globalPosition; _ = 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); // Filter out primary constructor references, as they are not useful to navigate to - we are already at the symbol declaration // This may also filter out other useful references, so may need to revisit later references = references.Where(s => s.LocationsArray.Length is not 0).ToImmutableArray(); 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 symbolLookupPopup = _symbolUsagePopupScene.Instantiate(); var ideReferenceLocationResults = await _roslynAnalysis.GetIdeReferenceLocationResults(locations); symbolLookupPopup.IdeReferenceLocationResults = ideReferenceLocationResults; symbolLookupPopup.Symbol = semanticInfo.Value.DeclaredSymbol; symbolLookupPopup.Size = new Vector2I(1, 1); // Set tiny size so it autosizes up based on child content await this.InvokeAsync(() => { AddChild(symbolLookupPopup); symbolLookupPopup.Position = new Vector2I((int)globalMousePosition.X - 5, (int)startSymbolCharGlobalPos.Y); symbolLookupPopup.Popup(); var currentMousePos = GetGlobalMousePosition(); Input.WarpMouse(currentMousePos with {X = currentMousePos.X + 1}); // it seems that until the mouse moves, behind the popup can still receive mouse events, which causes symbol the hover symbol popup to appear. }); } } } 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 } } }); } }