Auto size symbol lookup popup

This commit is contained in:
Matt Parker
2025-10-27 18:58:29 +10:00
parent 4e93a7ae6d
commit 308b3f319f
6 changed files with 59 additions and 52 deletions

View File

@@ -667,12 +667,30 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
return changedFilesWithText; return changedFilesWithText;
} }
public async Task<ISymbol?> GetEnclosingSymbolForReferenceLocation(ReferenceLocation referenceLocation) public record IdeReferenceLocationResult(ReferenceLocation ReferenceLocation, SharpIdeFile? File, ISymbol? EnclosingSymbol);
public async Task<IdeReferenceLocationResult?> GetIdeReferenceLocationResult(ReferenceLocation referenceLocation)
{ {
var semanticModel = await referenceLocation.Document.GetSemanticModelAsync(); var semanticModel = await referenceLocation.Document.GetSemanticModelAsync();
if (semanticModel is null) return null; if (semanticModel is null) return null;
var enclosingSymbol = ReferenceLocationExtensions.GetEnclosingMethodOrPropertyOrField(semanticModel, referenceLocation); var enclosingSymbol = ReferenceLocationExtensions.GetEnclosingMethodOrPropertyOrField(semanticModel, referenceLocation);
return enclosingSymbol; var lineSpan = referenceLocation.Location.GetMappedLineSpan();
var file = _sharpIdeSolutionModel!.AllFiles.SingleOrDefault(f => f.Path == lineSpan.Path);
var result = new IdeReferenceLocationResult(referenceLocation, file!, enclosingSymbol);
return result;
}
public async Task<ImmutableArray<IdeReferenceLocationResult>> GetIdeReferenceLocationResults(ImmutableArray<ReferenceLocation> referenceLocations)
{
var results = new List<IdeReferenceLocationResult>();
foreach (var referenceLocation in referenceLocations)
{
var result = await GetIdeReferenceLocationResult(referenceLocation);
if (result is not null)
{
results.Add(result);
}
}
return results.ToImmutableArray();
} }
public async Task<ImmutableArray<ReferencedSymbol>> FindAllSymbolReferences(ISymbol symbol, CancellationToken cancellationToken = default) public async Task<ImmutableArray<ReferencedSymbol>> FindAllSymbolReferences(ISymbol symbol, CancellationToken cancellationToken = default)
@@ -682,7 +700,7 @@ public class RoslynAnalysis(ILogger<RoslynAnalysis> logger, BuildService buildSe
var solution = _workspace!.CurrentSolution; var solution = _workspace!.CurrentSolution;
var references = await SymbolFinder.FindReferencesAsync(symbol, solution, cancellationToken); var references = await SymbolFinder.FindReferencesAsync(symbol, solution, cancellationToken);
return references.AsImmutable(); return references.ToImmutableArray();
} }
public async Task<(ISymbol?, LinePositionSpan?, TokenSemanticInfo?)> LookupSymbolSemanticInfo(SharpIdeFile fileModel, LinePosition linePosition) public async Task<(ISymbol?, LinePositionSpan?, TokenSemanticInfo?)> LookupSymbolSemanticInfo(SharpIdeFile fileModel, LinePosition linePosition)

View File

@@ -48,20 +48,15 @@ public partial class SharpIdeCodeEdit
else else
{ {
// Show popup to select which reference to go to // Show popup to select which reference to go to
var scene = _symbolUsagePopupScene.Instantiate<SymbolLookupPopup>(); var symbolLookupPopup = _symbolUsagePopupScene.Instantiate<SymbolLookupPopup>();
var locationsAndFiles = locations.Select(s => var ideReferenceLocationResults = await _roslynAnalysis.GetIdeReferenceLocationResults(locations);
{ symbolLookupPopup.IdeReferenceLocationResults = ideReferenceLocationResults;
var lineSpan = s.Location.GetMappedLineSpan(); symbolLookupPopup.Symbol = semanticInfo.Value.DeclaredSymbol;
var file = Solution!.AllFiles.SingleOrDefault(f => f.Path == lineSpan.Path); symbolLookupPopup.Size = new Vector2I(1, 1); // Set tiny size so it autosizes up based on child content
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(() => await this.InvokeAsync(() =>
{ {
AddChild(scene); AddChild(symbolLookupPopup);
scene.PopupCenteredClamped(); symbolLookupPopup.PopupCentered();
}); });
} }
} }

View File

@@ -2,6 +2,7 @@ using System.Collections.Immutable;
using Godot; using Godot;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
namespace SharpIDE.Godot.Features.SymbolLookup; namespace SharpIDE.Godot.Features.SymbolLookup;
@@ -10,8 +11,8 @@ public partial class SymbolLookupPopup : PopupPanel
{ {
private Label _symbolNameLabel = null!; private Label _symbolNameLabel = null!;
private VBoxContainer _usagesContainer = null!; private VBoxContainer _usagesContainer = null!;
public ImmutableArray<ReferenceLocation> Locations { get; set; }
public ImmutableArray<(ReferenceLocation location, SharpIdeFile file)> LocationsAndFiles { get; set; } public ImmutableArray<RoslynAnalysis.IdeReferenceLocationResult> IdeReferenceLocationResults { get; set; }
public ISymbol Symbol { get; set; } = null!; public ISymbol Symbol { get; set; } = null!;
private readonly PackedScene _symbolUsageScene = ResourceLoader.Load<PackedScene>("uid://dokm0dyac2enh"); private readonly PackedScene _symbolUsageScene = ResourceLoader.Load<PackedScene>("uid://dokm0dyac2enh");
@@ -24,11 +25,12 @@ public partial class SymbolLookupPopup : PopupPanel
AboutToPopup += OnAboutToPopup; AboutToPopup += OnAboutToPopup;
_usagesContainer.GetChildren().ToList().ForEach(s => s.QueueFree()); _usagesContainer.GetChildren().ToList().ForEach(s => s.QueueFree());
foreach (var (location, file) in LocationsAndFiles) foreach (var result in IdeReferenceLocationResults)
{ {
var resultNode = _symbolUsageScene.Instantiate<SymbolUsageComponent>(); var resultNode = _symbolUsageScene.Instantiate<SymbolUsageComponent>();
resultNode.Location = location; resultNode.Location = result.ReferenceLocation;
resultNode.File = file; resultNode.File = result.File;
resultNode.EnclosingSymbol = result.EnclosingSymbol;
resultNode.ParentSearchWindow = this; resultNode.ParentSearchWindow = this;
_usagesContainer.AddChild(resultNode); _usagesContainer.AddChild(resultNode);
} }

View File

@@ -14,7 +14,7 @@ shadow_size = 4
[node name="SymbolLookupPopup" type="PopupPanel"] [node name="SymbolLookupPopup" type="PopupPanel"]
oversampling_override = 1.0 oversampling_override = 1.0
size = Vector2i(505, 340) size = Vector2i(564, 340)
visible = true visible = true
theme_override_styles/panel = SubResource("StyleBoxFlat_cuaw5") theme_override_styles/panel = SubResource("StyleBoxFlat_cuaw5")
script = ExtResource("1_f5udm") script = ExtResource("1_f5udm")
@@ -50,15 +50,10 @@ unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "'UseWebApi()'" text = "'UseWebApi()'"
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"] [node name="UsagesVBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
follow_focus = true
[node name="UsagesVBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="SymbolUsageComponent" parent="MarginContainer/VBoxContainer/ScrollContainer/UsagesVBoxContainer" instance=ExtResource("1_k5g0h")] [node name="SymbolUsageComponent" parent="MarginContainer/VBoxContainer/UsagesVBoxContainer" instance=ExtResource("1_k5g0h")]
layout_mode = 2 layout_mode = 2

View File

@@ -1,4 +1,5 @@
using Godot; using Godot;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.FindSymbols;
using SharpIDE.Application.Features.Analysis; using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
@@ -14,9 +15,8 @@ public partial class SymbolUsageComponent : MarginContainer
public SymbolLookupPopup ParentSearchWindow { get; set; } = null!; public SymbolLookupPopup ParentSearchWindow { get; set; } = null!;
public ReferenceLocation? Location { get; set; } public ReferenceLocation? Location { get; set; }
public SharpIdeFile File { get; set; } = null!; public ISymbol? EnclosingSymbol { get; set; } = null!;
public SharpIdeFile? File { get; set; } = null!;
[Inject] private readonly RoslynAnalysis _roslynAnalysis = null!;
public override void _Ready() public override void _Ready()
{ {
@@ -32,7 +32,10 @@ public partial class SymbolUsageComponent : MarginContainer
{ {
var mappedLineSpan = Location!.Value.Location.GetMappedLineSpan(); var mappedLineSpan = Location!.Value.Location.GetMappedLineSpan();
var fileLinePosition = new SharpIdeFileLinePosition { Line = mappedLineSpan.StartLinePosition.Line, Column = mappedLineSpan.StartLinePosition.Character }; var fileLinePosition = new SharpIdeFileLinePosition { Line = mappedLineSpan.StartLinePosition.Line, Column = mappedLineSpan.StartLinePosition.Character };
GodotGlobalEvents.Instance.FileExternallySelected.InvokeParallelFireAndForget(File, fileLinePosition); if (File is not null)
{
GodotGlobalEvents.Instance.FileExternallySelected.InvokeParallelFireAndForget(File, fileLinePosition);
}
ParentSearchWindow.Hide(); ParentSearchWindow.Hide();
} }
@@ -41,13 +44,8 @@ public partial class SymbolUsageComponent : MarginContainer
if (result is null) return; if (result is null) return;
var mappedLineSpan = result.Value.Location.GetMappedLineSpan(); var mappedLineSpan = result.Value.Location.GetMappedLineSpan();
_fileNameLabel.Text = File.Name; _fileNameLabel.Text = File?.Name ?? Path.GetFileName(mappedLineSpan.Path);
_lineNumberLabel.Text = (mappedLineSpan.StartLinePosition.Line + 1).ToString(); _lineNumberLabel.Text = (mappedLineSpan.StartLinePosition.Line + 1).ToString();
_enclosingSymbolLabel.Text = EnclosingSymbol?.Name;
_ = Task.GodotRun(async () =>
{
var enclosingSymbol = await _roslynAnalysis.GetEnclosingSymbolForReferenceLocation(result.Value);
await this.InvokeAsync(() => _enclosingSymbolLabel.Text = enclosingSymbol?.Name);
});
} }
} }

View File

@@ -29,33 +29,32 @@ layout_mode = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_6ov2c") theme_override_styles/normal = SubResource("StyleBoxFlat_6ov2c")
theme_override_styles/focus = SubResource("StyleBoxFlat_dtmd4") theme_override_styles/focus = SubResource("StyleBoxFlat_dtmd4")
[node name="MarginContainer" type="MarginContainer" parent="Button"] [node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 1 layout_mode = 2
anchors_preset = 15 mouse_filter = 0
anchor_right = 1.0 mouse_behavior_recursive = 1
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_left = 5 theme_override_constants/margin_left = 5
theme_override_constants/margin_right = 5 theme_override_constants/margin_right = 5
[node name="HBoxContainer" type="HBoxContainer" parent="Button/MarginContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2 layout_mode = 2
[node name="EnclosingSymbolLabel" type="Label" parent="Button/MarginContainer/HBoxContainer"] [node name="EnclosingSymbolLabel" type="Label" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3
text = "Containing Symbol Name" text = "Containing Symbol Name"
text_overrun_behavior = 3
[node name="FileNameLabel" type="Label" parent="Button/MarginContainer/HBoxContainer"] [node name="Spacer" type="Control" parent="MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="FileNameLabel" type="Label" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(1, 1, 1, 0.7411765) theme_override_colors/font_color = Color(1, 1, 1, 0.7411765)
text = "FileName.cs" text = "FileName.cs"
[node name="LineNumberLabel" type="Label" parent="Button/MarginContainer/HBoxContainer"] [node name="LineNumberLabel" type="Label" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(1, 1, 1, 0.7411765) theme_override_colors/font_color = Color(1, 1, 1, 0.7411765)