Finish typename search

This commit is contained in:
2026-03-15 13:59:09 -04:00
parent 9a2c4452d4
commit 7c9419f79e
5 changed files with 152 additions and 78 deletions

View File

@@ -1,62 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h"
#include "MCPFetcher.h"
#include "MCPUtils.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraphSchema_K2.h"
#include "GraphNode_PrintPinType.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_GraphNode_ListPinTypes : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
virtual FString GetDescription() const override
{
return TEXT("List every type available in the variable type selector menu, as Category|Item.");
}
virtual void Handle() override
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
TArray<TSharedPtr<UEdGraphSchema_K2::FPinTypeTreeInfo>> TypeTree;
Schema->GetVariableTypeTree(TypeTree, ETypeTreeFilter::None);
for (const TSharedPtr<UEdGraphSchema_K2::FPinTypeTreeInfo>& Root : TypeTree)
{
PrintTree(Root, FString());
}
}
private:
void PrintTree(const TSharedPtr<UEdGraphSchema_K2::FPinTypeTreeInfo>& Node, const FString& Category)
{
FString Desc = Node->GetDescription().ToString();
if (!Node->bReadOnly)
{
UObject *SCO = Node->GetPinTypeNoResolve().PinSubCategoryObject.Get();
if (SCO) {
UMCPServer::Printf(TEXT("SCO %s %s\n"), *SCO->GetPackage()->GetName(), *SCO->GetName());
} else {
auto cad = Node->GetCachedAssetData();
UMCPServer::Printf(TEXT("CAD %s %s\n"), *cad.PackageName.ToString(), *cad.AssetName.ToString());
}
}
FString ChildCategory = FString::Printf(TEXT("%s%s|"), *Category, *Desc);
for (const TSharedPtr<UEdGraphSchema_K2::FPinTypeTreeInfo>& Child : Node->Children)
{
PrintTree(Child, ChildCategory);
}
}
};

View File

@@ -0,0 +1,104 @@
#pragma once
#include "CoreMinimal.h"
#include "MCPServer.h"
#include "MCPHandler.h"
#include "MCPTypes.h"
#include "MCPUtils.h"
#include "AssetRegistry/AssetData.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "UObject/UObjectIterator.h"
#include "StructUtils/UserDefinedStruct.h"
#include "Engine/UserDefinedEnum.h"
#include "Engine/Blueprint.h"
#include "TypeName_Search.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UMCP_TypeName_Search : public UObject, public IMCPHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Substring filter for type names"))
FString Query;
UPROPERTY(meta=(Optional, Description="Maximum number of results"))
int32 Limit = 100;
virtual FString GetDescription() const override
{
return TEXT("Search for type names usable in pin type specifications. "
"Returns short names that can be used with commands like Blueprint_ChangeVariableType.");
}
void TryMatchObject(TSet<UObject*> &Matches, UObject *Obj)
{
if (!Obj) return;
FString Name = Obj->GetName();
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) return;
Matches.Add(Obj);
}
void TryMatchObjects(TSet<UObject*> &Matches, UClass *Class)
{
ForEachObjectOfClass(Class, [&](UObject *Obj){
if (Matches.Num() == Limit) return;
TryMatchObject(Matches, Obj);
}, true);
}
void TryMatchAssets(TSet<UObject*> &Matches, UClass *Class)
{
IAssetRegistry& Registry = *IAssetRegistry::Get();
TArray<FAssetData> AssetResults;
Registry.GetAssetsByClass(Class->GetClassPathName(), AssetResults, true);
for (const FAssetData& Data : AssetResults)
{
if (Matches.Num() == Limit) return;
FString Name = Data.AssetName.ToString();
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) continue;
UObject *Obj = Data.GetAsset();
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
Obj = BP->GeneratedClass;
TryMatchObject(Matches, Obj);
}
}
virtual void Handle() override
{
TSet<UObject*> Matches;
TryMatchObjects(Matches, UScriptStruct::StaticClass());
TryMatchObjects(Matches, UClass::StaticClass());
TryMatchObjects(Matches, UEnum::StaticClass());
TryMatchAssets(Matches, UBlueprint::StaticClass());
TryMatchAssets(Matches, UUserDefinedStruct::StaticClass());
TryMatchAssets(Matches, UUserDefinedEnum::StaticClass());
TArray<FString> Results;
for (const UObject *Obj : Matches)
{
const TCHAR *Kind = TEXT("Unknown");
if (Cast<UEnum>(Obj))
Kind = TEXT("Enum");
else if (Cast<UScriptStruct>(Obj))
Kind = TEXT("Struct");
else if (const UClass* Class = Cast<UClass>(Obj))
Kind = Class->IsChildOf(UInterface::StaticClass()) ? TEXT("Interface") : TEXT("Class");
Results.Add(FString::Printf(TEXT("%s %s\n"), Kind, *UMCPTypes::TypeToText(Obj)));
}
Results.Sort();
for (const auto &Result : Results)
{
UMCPServer::Print(Result);
}
if (Results.Num() == Limit)
{
UMCPServer::Printf(TEXT("Search limit reached, raise it with Limit=\n"));
}
}
};

View File

@@ -9,13 +9,27 @@
// Choose Short Name
// ---------------------------------------------------------------------------
FString UMCPTypes::GetNameWithoutUnderscoreC(const UObject *Obj)
{
FString Name = Obj->GetName();
if (Name.EndsWith(TEXT("_C")))
{
if (const UClass* Class = Cast<UClass>(Obj))
{
if (Class->ClassGeneratedBy != nullptr)
Name.LeftChopInline(2);
}
}
return Name;
}
void UMCPTypes::ReserveShortName(FName Name)
{
FString NameStr = Name.ToString();
ShortToPath[NameStr.ToLower()] = TEXT("PRIMITIVE");
ShortToPath.Add(NameStr.ToLower(), FString(TEXT("PRIMITIVE")));
}
FString UMCPTypes::ChooseShortName(UObject* Obj)
FString UMCPTypes::ChooseShortName(const UObject* Obj)
{
if (!Cast<UScriptStruct>(Obj) && !Cast<UClass>(Obj) && !Cast<UEnum>(Obj))
return FString();
@@ -24,8 +38,7 @@ FString UMCPTypes::ChooseShortName(UObject* Obj)
FString *OldShort = PathToShort.Find(Path);
if (OldShort != nullptr) return *OldShort;
FString Name = Obj->GetName();
if (Name.EndsWith(TEXT("_C"))) Name.LeftChopInline(2);
FString Name = GetNameWithoutUnderscoreC(Obj);
FString Lower = Name.ToLower();
if (!ShortToPath.Contains(Lower))
@@ -40,7 +53,7 @@ FString UMCPTypes::ChooseShortName(UObject* Obj)
FString NumberedLower = FString::Printf(TEXT("%s%d"), *Lower, i);
if (!ShortToPath.Contains(NumberedLower))
{
FString NumberedName = FString::Printf(TEXT("%s%d"), *Name, i);
FString NumberedName = FString::Printf(TEXT("%s_%d"), *Name, i);
ShortToPath.Add(NumberedLower, Path);
PathToShort.Add(Path, NumberedName);
return NumberedName;
@@ -59,31 +72,34 @@ void UMCPTypes::ChooseShortNames(UPackage* Package)
}, false);
}
// ---------------------------------------------------------------------------
// TypeToText
// ---------------------------------------------------------------------------
FString UMCPTypes::TypeToTextInner(FName Category, FName SubCategory, UObject* SubCategoryObject)
{
if (Category == UEdGraphSchema_K2::PC_Boolean) return TEXT("bool");
if (Category == UEdGraphSchema_K2::PC_Int) return TEXT("int32");
if (Category == UEdGraphSchema_K2::PC_Int64) return TEXT("int64");
if (Category == UEdGraphSchema_K2::PC_Name) return TEXT("Name");
if (Category == UEdGraphSchema_K2::PC_String) return TEXT("String");
if (Category == UEdGraphSchema_K2::PC_Text) return TEXT("Text");
if ((Category == UEdGraphSchema_K2::PC_Boolean) ||
(Category == UEdGraphSchema_K2::PC_Int) ||
(Category == UEdGraphSchema_K2::PC_Int64) ||
(Category == UEdGraphSchema_K2::PC_Name) ||
(Category == UEdGraphSchema_K2::PC_String) ||
(Category == UEdGraphSchema_K2::PC_Text))
{
return Category.ToString();
}
if (Category == UEdGraphSchema_K2::PC_Real)
{
if (SubCategory == UEdGraphSchema_K2::PC_Float)
return TEXT("float");
return TEXT("double");
return SubCategory.ToString();
}
if (Category == UEdGraphSchema_K2::PC_Byte)
{
if (SubCategoryObject)
return ChooseShortName(SubCategoryObject);
return TEXT("uint8");
return Category.ToString();
}
if (Category == UEdGraphSchema_K2::PC_Enum)
@@ -155,6 +171,13 @@ FString UMCPTypes::TypeToText(const FProperty *Property)
}
}
FString UMCPTypes::TypeToText(const UObject* Obj)
{
UMCPTypes* Types = GEditor->GetEditorSubsystem<UMCPTypes>();
if (!Types) return FString();
return Types->ChooseShortName(Obj);
}
// ---------------------------------------------------------------------------
// Subsystem lifecycle
// ---------------------------------------------------------------------------

View File

@@ -22,6 +22,10 @@ public:
static FString TypeToText(const FEdGraphPinType& PinType);
static FString TypeToText(const FProperty *Property);
// Get the short name for a UClass, UScriptStruct, or UEnum.
// Returns empty string if the object is not one of those types.
static FString TypeToText(const UObject* Obj);
// Try to parse a type. If there's a problem, returns an error
// message. If all goes well, returns empty string.
static FString TryTextToType(const FString& Text, FEdGraphPinType& OutPinType);
@@ -35,6 +39,10 @@ private:
bool ResolvePath(const FString &Name, const FString &Path, FEdGraphPinType &OutType);
// Get the object's name, not including the _C generated
// by the blueprint compiler for generated classes.
static FString GetNameWithoutUnderscoreC(const UObject *Obj);
// Reserve the short name for a primitive type.
// The value stored in the map is just "PRIMITIVE".
void ReserveShortName(FName Name);
@@ -43,7 +51,7 @@ private:
// choose the same name for two classes. If the object already has
// a short name, return it. If it's not a class, struct, enum, or
// interface, return empty string.
FString ChooseShortName(UObject* Obj);
FString ChooseShortName(const UObject* Obj);
// Choose short names for every type in the package.
void ChooseShortNames(UPackage* Package);

View File

@@ -185,3 +185,4 @@ private:
static void AppendNumericSuffix(FString &Name, int32 N);
static FString SetPropertyFromJson(void* Container, FProperty* Prop, const FString& FieldName, const FJsonObject* Json);
};