This commit is contained in:
Matt Parker
2025-10-12 12:24:09 +10:00
parent de3a950327
commit 00374034c0
4 changed files with 234 additions and 204 deletions

View File

@@ -132,7 +132,7 @@ public partial class SharpIdeCodeEdit : CodeEdit
IFieldSymbol fieldSymbol => SymbolInfoComponents.GetFieldSymbolInfo(fieldSymbol),
IParameterSymbol parameterSymbol => SymbolInfoComponents.GetParameterSymbolInfo(parameterSymbol),
ILocalSymbol localSymbol => SymbolInfoComponents.GetLocalVariableSymbolInfo(localSymbol),
_ => new Control()
_ => SymbolInfoComponents.GetUnknownTooltip(roslynSymbol)
};
popupPanel.AddChild(symbolInfoNode);
AddChild(popupPanel);

View File

@@ -1 +0,0 @@
uid://c48r2nff1ckv

View File

@@ -9,7 +9,8 @@ namespace SharpIDE.Godot.Features.CodeEditor;
public static partial class SymbolInfoComponents
{
private static readonly FontVariation MonospaceFont = ResourceLoader.Load<FontVariation>("uid://cctwlwcoycek7");
public static RichTextLabel GetMethodSymbolInfo(IMethodSymbol methodSymbol)
public static Control GetUnknownTooltip(ISymbol symbol)
{
var label = new RichTextLabel();
label.FitContent = true;
@@ -17,25 +18,18 @@ public static partial class SymbolInfoComponents
label.SetAnchorsPreset(Control.LayoutPreset.FullRect);
label.PushColor(CachedColors.White);
label.PushFont(MonospaceFont);
label.AddAttributes(methodSymbol);
label.AddAccessibilityModifier(methodSymbol);
label.AddMethodStaticModifier(methodSymbol);
label.AddVirtualModifier(methodSymbol);
label.AddAbstractModifier(methodSymbol);
label.AddOverrideModifier(methodSymbol);
label.AddMethodReturnType(methodSymbol);
label.AddText(" ");
label.AddMethodName(methodSymbol);
label.AddTypeParameters(methodSymbol);
label.AddText("(");
label.AddParameters(methodSymbol);
label.AddText(")");
label.AddContainingNamespaceAndClass(methodSymbol);
label.AddText($"UNHANDLED SYMBOL TYPE: {symbol.GetType().Name} - please create an issue!");
label.Newline();
label.AddText(symbol.Kind.ToString());
label.AddText(" ");
label.AddText(symbol.Name);
label.Newline();
label.AddContainingNamespaceAndClass(symbol);
label.Newline();
label.AddTypeParameterArguments(methodSymbol);
label.Pop(); // font
label.AddDocs(methodSymbol);
label.Pop(); // default white
label.AddDocs(symbol);
label.Pop();
return label;
}
@@ -58,17 +52,6 @@ public static partial class SymbolInfoComponents
label.Pop();
}
private static void AddMethodStaticModifier(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.IsStatic || methodSymbol.ReducedFrom?.IsStatic is true)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("static");
label.Pop();
label.AddText(" ");
}
}
private static void AddOverrideModifier(this RichTextLabel label, ISymbol methodSymbol)
{
if (methodSymbol.IsOverride)
@@ -112,156 +95,6 @@ public static partial class SymbolInfoComponents
}
}
private static void AddMethodReturnType(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.ReturnsVoid)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("void");
label.Pop();
return;
}
label.PushColor(CachedColors.ClassGreen);
label.AddText(methodSymbol.ReturnType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
}
private static void AddMethodName(this RichTextLabel label, IMethodSymbol methodSymbol)
{
label.PushColor(CachedColors.Yellow);
label.AddText(methodSymbol.Name);
label.Pop();
}
private static void AddTypeParameters(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.TypeParameters.Length == 0) return;
label.PushColor(CachedColors.White);
label.AddText("<");
label.Pop();
foreach (var (index, typeParameter) in methodSymbol.TypeParameters.Index())
{
label.PushColor(CachedColors.ClassGreen);
label.AddText(typeParameter.Name);
label.Pop();
if (index < methodSymbol.TypeParameters.Length - 1)
{
label.AddText(", ");
}
}
label.PushColor(CachedColors.White);
label.AddText(">");
label.Pop();
}
private static void AddParameters(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.IsExtensionMethod)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("this");
label.Pop();
label.AddText(" ");
}
var parameters = methodSymbol.ReducedFrom?.Parameters ?? methodSymbol.Parameters;
foreach (var (index, parameterSymbol) in parameters.Index())
{
var attributes = parameterSymbol.GetAttributes();
if (attributes.Length is not 0)
{
foreach (var (attrIndex, attribute) in attributes.Index())
{
label.AddAttribute(attribute, false);
}
}
if (parameterSymbol.RefKind != RefKind.None) // ref, in, out
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText(parameterSymbol.RefKind.ToString().ToLower());
label.Pop();
label.AddText(" ");
}
else if (parameterSymbol.IsParams)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("params");
label.Pop();
label.AddText(" ");
}
label.PushColor(parameterSymbol.Type.GetSymbolColourByType());
label.AddText(parameterSymbol.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
label.AddText(" ");
label.PushColor(CachedColors.VariableBlue);
label.AddText(parameterSymbol.Name);
label.Pop();
// default value
if (parameterSymbol.HasExplicitDefaultValue)
{
label.AddText(" = ");
if (parameterSymbol.ExplicitDefaultValue is null)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("null");
label.Pop();
}
else if (parameterSymbol.Type.TypeKind == TypeKind.Enum)
{
var explicitDefaultValue = parameterSymbol.ExplicitDefaultValue;
// Find the enum field with the same constant value
var enumMember = parameterSymbol.Type.GetMembers()
.OfType<IFieldSymbol>()
.FirstOrDefault(f => f.HasConstantValue && Equals(f.ConstantValue, explicitDefaultValue));
if (enumMember != null)
{
label.PushColor(CachedColors.InterfaceGreen);
label.AddText(parameterSymbol.Type.Name);
label.Pop();
label.PushColor(CachedColors.White);
label.AddText(".");
label.Pop();
label.PushColor(CachedColors.White);
label.AddText(enumMember.Name);
label.Pop();
}
else
{
label.PushColor(CachedColors.InterfaceGreen);
label.AddText(parameterSymbol.Type.Name);
label.Pop();
label.AddText($"({explicitDefaultValue})");
}
}
else if (parameterSymbol.ExplicitDefaultValue is string str)
{
label.PushColor(CachedColors.LightOrangeBrown);
label.AddText($"""
"{str}"
""");
label.Pop();
}
else if (parameterSymbol.ExplicitDefaultValue is bool b)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText(b ? "true" : "false");
label.Pop();
}
else
{
label.AddText(parameterSymbol.ExplicitDefaultValue.ToString() ?? "unknown");
}
}
if (index < parameters.Length - 1)
{
label.AddText(", ");
}
}
}
private static void AddContainingNamespaceAndClass(this RichTextLabel label, ISymbol symbol)
{
if (symbol.ContainingNamespace is null || symbol.ContainingNamespace.IsGlobalNamespace) return; // might be wrong
@@ -293,29 +126,6 @@ public static partial class SymbolInfoComponents
label.Pop(); // meta
}
private static void AddTypeParameterArguments(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.TypeArguments.Length == 0) return;
label.Newline(); // TODO: Make this only 0.5 lines high
var typeParameters = methodSymbol.TypeParameters;
var typeArguments = methodSymbol.TypeArguments;
if (typeParameters.Length != typeArguments.Length) throw new Exception("Type parameters and type arguments length mismatch.");
foreach (var (index, (typeArgument, typeParameter)) in methodSymbol.TypeArguments.Zip(typeParameters).Index())
{
label.PushColor(CachedColors.ClassGreen);
label.AddText(typeParameter.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
label.AddText(" is ");
label.PushColor(typeArgument.GetSymbolColourByType());
label.AddText(typeArgument.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
if (index < methodSymbol.TypeArguments.Length - 1)
{
label.Newline();
}
}
}
private static void AddAttribute(this RichTextLabel label, AttributeData attribute, bool newLines)
{
label.AddText("[");

View File

@@ -0,0 +1,221 @@
using Godot;
using Microsoft.CodeAnalysis;
namespace SharpIDE.Godot.Features.CodeEditor;
public static partial class SymbolInfoComponents
{
public static RichTextLabel GetMethodSymbolInfo(IMethodSymbol methodSymbol)
{
var label = new RichTextLabel();
label.FitContent = true;
label.AutowrapMode = TextServer.AutowrapMode.Off;
label.SetAnchorsPreset(Control.LayoutPreset.FullRect);
label.PushColor(CachedColors.White);
label.PushFont(MonospaceFont);
label.AddAttributes(methodSymbol);
label.AddAccessibilityModifier(methodSymbol);
label.AddMethodStaticModifier(methodSymbol);
label.AddVirtualModifier(methodSymbol);
label.AddAbstractModifier(methodSymbol);
label.AddOverrideModifier(methodSymbol);
label.AddMethodReturnType(methodSymbol);
label.AddText(" ");
label.AddMethodName(methodSymbol);
label.AddTypeParameters(methodSymbol);
label.AddText("(");
label.AddParameters(methodSymbol);
label.AddText(")");
label.AddContainingNamespaceAndClass(methodSymbol);
label.Newline();
label.AddTypeParameterArguments(methodSymbol);
label.Pop(); // font
label.AddDocs(methodSymbol);
label.Pop(); // default white
return label;
}
private static void AddMethodStaticModifier(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.IsStatic || methodSymbol.ReducedFrom?.IsStatic is true)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("static");
label.Pop();
label.AddText(" ");
}
}
private static void AddMethodReturnType(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.ReturnsVoid)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("void");
label.Pop();
return;
}
label.PushColor(CachedColors.ClassGreen);
label.AddText(methodSymbol.ReturnType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
}
private static void AddMethodName(this RichTextLabel label, IMethodSymbol methodSymbol)
{
label.PushColor(CachedColors.Yellow);
label.AddText(methodSymbol.Name);
label.Pop();
}
private static void AddTypeParameters(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.TypeParameters.Length == 0) return;
label.PushColor(CachedColors.White);
label.AddText("<");
label.Pop();
foreach (var (index, typeParameter) in methodSymbol.TypeParameters.Index())
{
label.PushColor(CachedColors.ClassGreen);
label.AddText(typeParameter.Name);
label.Pop();
if (index < methodSymbol.TypeParameters.Length - 1)
{
label.AddText(", ");
}
}
label.PushColor(CachedColors.White);
label.AddText(">");
label.Pop();
}
private static void AddParameters(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.IsExtensionMethod)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("this");
label.Pop();
label.AddText(" ");
}
var parameters = methodSymbol.ReducedFrom?.Parameters ?? methodSymbol.Parameters;
foreach (var (index, parameterSymbol) in parameters.Index())
{
var attributes = parameterSymbol.GetAttributes();
if (attributes.Length is not 0)
{
foreach (var (attrIndex, attribute) in attributes.Index())
{
label.AddAttribute(attribute, false);
}
}
if (parameterSymbol.RefKind != RefKind.None) // ref, in, out
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText(parameterSymbol.RefKind.ToString().ToLower());
label.Pop();
label.AddText(" ");
}
else if (parameterSymbol.IsParams)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("params");
label.Pop();
label.AddText(" ");
}
label.PushColor(parameterSymbol.Type.GetSymbolColourByType());
label.AddText(parameterSymbol.Type.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
label.AddText(" ");
label.PushColor(CachedColors.VariableBlue);
label.AddText(parameterSymbol.Name);
label.Pop();
// default value
if (parameterSymbol.HasExplicitDefaultValue)
{
label.AddText(" = ");
if (parameterSymbol.ExplicitDefaultValue is null)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText("null");
label.Pop();
}
else if (parameterSymbol.Type.TypeKind == TypeKind.Enum)
{
var explicitDefaultValue = parameterSymbol.ExplicitDefaultValue;
// Find the enum field with the same constant value
var enumMember = parameterSymbol.Type.GetMembers()
.OfType<IFieldSymbol>()
.FirstOrDefault(f => f.HasConstantValue && Equals(f.ConstantValue, explicitDefaultValue));
if (enumMember != null)
{
label.PushColor(CachedColors.InterfaceGreen);
label.AddText(parameterSymbol.Type.Name);
label.Pop();
label.PushColor(CachedColors.White);
label.AddText(".");
label.Pop();
label.PushColor(CachedColors.White);
label.AddText(enumMember.Name);
label.Pop();
}
else
{
label.PushColor(CachedColors.InterfaceGreen);
label.AddText(parameterSymbol.Type.Name);
label.Pop();
label.AddText($"({explicitDefaultValue})");
}
}
else if (parameterSymbol.ExplicitDefaultValue is string str)
{
label.PushColor(CachedColors.LightOrangeBrown);
label.AddText($"""
"{str}"
""");
label.Pop();
}
else if (parameterSymbol.ExplicitDefaultValue is bool b)
{
label.PushColor(CachedColors.KeywordBlue);
label.AddText(b ? "true" : "false");
label.Pop();
}
else
{
label.AddText(parameterSymbol.ExplicitDefaultValue.ToString() ?? "unknown");
}
}
if (index < parameters.Length - 1)
{
label.AddText(", ");
}
}
}
private static void AddTypeParameterArguments(this RichTextLabel label, IMethodSymbol methodSymbol)
{
if (methodSymbol.TypeArguments.Length == 0) return;
label.Newline(); // TODO: Make this only 0.5 lines high
var typeParameters = methodSymbol.TypeParameters;
var typeArguments = methodSymbol.TypeArguments;
if (typeParameters.Length != typeArguments.Length) throw new Exception("Type parameters and type arguments length mismatch.");
foreach (var (index, (typeArgument, typeParameter)) in methodSymbol.TypeArguments.Zip(typeParameters).Index())
{
label.PushColor(CachedColors.ClassGreen);
label.AddText(typeParameter.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
label.AddText(" is ");
label.PushColor(typeArgument.GetSymbolColourByType());
label.AddText(typeArgument.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
label.Pop();
if (index < methodSymbol.TypeArguments.Length - 1)
{
label.Newline();
}
}
}
}