Name sanitization overhaul in progress

This commit is contained in:
2026-03-28 21:07:28 -04:00
parent 22fe60f431
commit 9790fa34e1
6 changed files with 41 additions and 71 deletions

View File

@@ -44,7 +44,7 @@ bool UWingComponentReference::CheckExists(UWingComponentReference::FoundComponen
{ {
if ((FC.SCS == nullptr) && (FC.Native == nullptr)) if ((FC.SCS == nullptr) && (FC.Native == nullptr))
{ {
UWingServer::Printf(TEXT("Cannot find component: %s\n"), *WingUtils::SanitizeName(FC.Name)); UWingServer::Printf(TEXT("Cannot find component: %s\n"), *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
return true; return true;
@@ -55,19 +55,19 @@ bool UWingComponentReference::CheckValidParent(UWingComponentReference::FoundCom
if (FC.SCS && FC.Native) if (FC.SCS && FC.Native)
{ {
UWingServer::Printf(TEXT("Weirdness, two components named %s\n"), UWingServer::Printf(TEXT("Weirdness, two components named %s\n"),
*WingUtils::SanitizeName(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
if ((FC.SCS == nullptr) && (FC.Native == nullptr)) if ((FC.SCS == nullptr) && (FC.Native == nullptr))
{ {
UWingServer::Printf(TEXT("No such parent component: %s\n"), UWingServer::Printf(TEXT("No such parent component: %s\n"),
*WingUtils::SanitizeName(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
if (FC.Native && (!Cast<USceneComponent>(FC.Native))) if (FC.Native && (!Cast<USceneComponent>(FC.Native)))
{ {
UWingServer::Printf(TEXT("Not a SceneComponent, so cannot be parent: %s\n"), UWingServer::Printf(TEXT("Not a SceneComponent, so cannot be parent: %s\n"),
*WingUtils::SanitizeName(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
return true; return true;
@@ -78,7 +78,7 @@ bool UWingComponentReference::CheckNoSuchComponent(FoundComponent FC)
if (FC.SCS || FC.Native) if (FC.SCS || FC.Native)
{ {
UWingServer::Printf(TEXT("A component named %s already exists"), UWingServer::Printf(TEXT("A component named %s already exists"),
*WingUtils::SanitizeName(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
return true; return true;
@@ -89,7 +89,7 @@ bool UWingComponentReference::CheckNotNative(FoundComponent FC, const TCHAR *Act
if (FC.Native != nullptr) if (FC.Native != nullptr)
{ {
UWingServer::Printf(TEXT("Component %s is native, cannot %s native components"), UWingServer::Printf(TEXT("Component %s is native, cannot %s native components"),
*WingUtils::SanitizeName(FC.Name), Action); *WingUtils::ExternalizeID(FC.Name), Action);
return false; return false;
} }
return true; return true;
@@ -100,7 +100,7 @@ bool UWingComponentReference::CheckOwnedByBlueprint(FoundComponent FC, UBlueprin
if ((FC.SCS == nullptr) || (FC.SCS->GetSCS() != BP->SimpleConstructionScript)) if ((FC.SCS == nullptr) || (FC.SCS->GetSCS() != BP->SimpleConstructionScript))
{ {
UWingServer::Printf(TEXT("Component %s belongs to blueprint %s, edit that blueprint instead"), UWingServer::Printf(TEXT("Component %s belongs to blueprint %s, edit that blueprint instead"),
*WingUtils::SanitizeName(FC.Name), *WingUtils::FormatName(FC.SCS->GetSCS()->GetBlueprint())); *WingUtils::ExternalizeID(FC.Name), *WingUtils::FormatName(FC.SCS->GetSCS()->GetBlueprint()));
return false; return false;
} }
return true; return true;
@@ -151,7 +151,7 @@ bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingC
if (NewNode == nullptr) if (NewNode == nullptr)
{ {
UWingServer::Printf(TEXT("Could not create new component %s of class %s, unknown reason.\n"), UWingServer::Printf(TEXT("Could not create new component %s of class %s, unknown reason.\n"),
*WingUtils::SanitizeName(Name), *WingUtils::FormatName(Class)); *WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class));
return false; return false;
} }
@@ -262,7 +262,7 @@ TArray<UWingComponentReference*> UWingComponentReference::GetAll(UBlueprint* BP)
Ref->TypeName = UWingTypes::TypeToText(Comp->GetClass()); Ref->TypeName = UWingTypes::TypeToText(Comp->GetClass());
if (USceneComponent* Scene = Cast<USceneComponent>(Comp)) if (USceneComponent* Scene = Cast<USceneComponent>(Comp))
if (USceneComponent* AttachParent = Scene->GetAttachParent()) if (USceneComponent* AttachParent = Scene->GetAttachParent())
Ref->ParentName = WingUtils::SanitizeName(AttachParent->GetFName()); Ref->ParentName = WingUtils::ExternalizeID(AttachParent->GetFName());
Ref->Native = true; Ref->Native = true;
Ref->Inherited = true; Ref->Inherited = true;
Result.Add(Ref); Result.Add(Ref);
@@ -281,7 +281,7 @@ TArray<UWingComponentReference*> UWingComponentReference::GetAll(UBlueprint* BP)
Ref->BP = BP; Ref->BP = BP;
Ref->VariableName = Node->GetVariableName(); Ref->VariableName = Node->GetVariableName();
Ref->TypeName = UWingTypes::TypeToText(Node->ComponentClass); Ref->TypeName = UWingTypes::TypeToText(Node->ComponentClass);
Ref->ParentName = WingUtils::SanitizeName(ParentNames[Node->GetVariableName()]); Ref->ParentName = WingUtils::ExternalizeID(ParentNames[Node->GetVariableName()]);
Ref->Native = false; Ref->Native = false;
Ref->Inherited = (WalkBP != BP); Ref->Inherited = (WalkBP != BP);
Result.Add(Ref); Result.Add(Ref);

View File

@@ -168,7 +168,7 @@ FString UWingTypes::NewShortName(const FString &Path, FName PinCategory, const U
if ((PinCategory == UEdGraphSchema_K2::PC_Object || PinCategory == UEdGraphSchema_K2::PC_Interface) if ((PinCategory == UEdGraphSchema_K2::PC_Object || PinCategory == UEdGraphSchema_K2::PC_Interface)
&& IsUserDefined && Proposal.EndsWith(TEXT("_C"))) && IsUserDefined && Proposal.EndsWith(TEXT("_C")))
Proposal.LeftChopInline(2); Proposal.LeftChopInline(2);
Proposal = WingUtils::SanitizeName(Proposal); Proposal = WingUtils::ExternalizeID(Proposal);
check(!Proposal.IsEmpty()); check(!Proposal.IsEmpty());
// Construct the Info struct. // Construct the Info struct.

View File

@@ -55,18 +55,12 @@
// Name sanitization // Name sanitization
// ============================================================ // ============================================================
FString WingUtils::SanitizeName(const FString &InName) FString WingUtils::ExternalizeID(const FString &InName)
{ {
return WingTokenizer::ExternalizeID(InName); return WingTokenizer::ExternalizeID(InName);
} }
FString WingUtils::UnsanitizeName(const FString &InName) FString WingUtils::ExternalizeID(FName Name)
{
FString Error;
return WingTokenizer::TryInternalizeID(InName, Error);
}
FString WingUtils::SanitizeName(FName Name)
{ {
return WingTokenizer::ExternalizeID(Name.ToString()); return WingTokenizer::ExternalizeID(Name.ToString());
} }
@@ -111,7 +105,6 @@ FString WingUtils::StandardizeMenuItem(const FString &Item)
} }
// ============================================================ // ============================================================
// Name Lookup // Name Lookup
// ============================================================ // ============================================================
@@ -128,49 +121,49 @@ FString WingUtils::FormatName(const UBlueprint *BP)
FString WingUtils::FormatName(const UActorComponent *C) FString WingUtils::FormatName(const UActorComponent *C)
{ {
return SanitizeName(C->GetName()); return ExternalizeID(C->GetName());
} }
FString WingUtils::FormatName(const USCS_Node *Node) FString WingUtils::FormatName(const USCS_Node *Node)
{ {
return SanitizeName(Node->GetVariableName()); return ExternalizeID(Node->GetVariableName());
} }
FString WingUtils::FormatName(const UEdGraph *Graph) FString WingUtils::FormatName(const UEdGraph *Graph)
{ {
return SanitizeName(Graph->GetName()); return ExternalizeID(Graph->GetName());
} }
FString WingUtils::FormatName(const TObjectPtr<UEdGraph> &Graph) FString WingUtils::FormatName(const TObjectPtr<UEdGraph> &Graph)
{ {
return SanitizeName(Graph->GetName()); return ExternalizeID(Graph->GetName());
} }
FString WingUtils::FormatName(const UEdGraphNode* Node) FString WingUtils::FormatName(const UEdGraphNode* Node)
{ {
return SanitizeName(Node->GetName()); return ExternalizeID(Node->GetName());
} }
FString WingUtils::FormatName(const UEdGraphPin *Pin) FString WingUtils::FormatName(const UEdGraphPin *Pin)
{ {
return SanitizeName(Pin->GetName()); return ExternalizeID(Pin->GetName());
} }
FString WingUtils::FormatName(const FMemberReference &Ref) FString WingUtils::FormatName(const FMemberReference &Ref)
{ {
return SanitizeName(Ref.GetMemberName()); return ExternalizeID(Ref.GetMemberName());
} }
FString WingUtils::FormatName(const FBPVariableDescription &Var) FString WingUtils::FormatName(const FBPVariableDescription &Var)
{ {
return SanitizeName(Var.VarName); return ExternalizeID(Var.VarName);
} }
FString WingUtils::FormatName(const UStruct *Struct) FString WingUtils::FormatName(const UStruct *Struct)
{ {
if (Cast<UScriptStruct>(Struct) || Cast<UClass>(Struct)) if (Cast<UScriptStruct>(Struct) || Cast<UClass>(Struct))
return UWingTypes::TypeToTextOrDie(Struct); return UWingTypes::TypeToTextOrDie(Struct);
return SanitizeName(Struct->GetName()); return ExternalizeID(Struct->GetName());
} }
FString WingUtils::FormatName(const UClass *Class) FString WingUtils::FormatName(const UClass *Class)
@@ -195,7 +188,7 @@ FString WingUtils::FormatName(const UMaterialFunction *MaterialFunction)
FString WingUtils::FormatName(const UMaterialExpression *Expression) FString WingUtils::FormatName(const UMaterialExpression *Expression)
{ {
return SanitizeName(Expression->GetName()); return ExternalizeID(Expression->GetName());
} }
FString WingUtils::FormatName(const UStaticMesh *Mesh) FString WingUtils::FormatName(const UStaticMesh *Mesh)
@@ -235,12 +228,12 @@ FString WingUtils::FormatName(const UEnum *Enum)
FString WingUtils::FormatName(const FProperty *Prop) FString WingUtils::FormatName(const FProperty *Prop)
{ {
return SanitizeName(Prop->GetName()); return ExternalizeID(Prop->GetName());
} }
FString WingUtils::FormatName(const FUserPinInfo &Pin) FString WingUtils::FormatName(const FUserPinInfo &Pin)
{ {
return SanitizeName(Pin.PinName); return ExternalizeID(Pin.PinName);
} }
FString WingUtils::FormatName(const FBPInterfaceDescription &IFace) FString WingUtils::FormatName(const FBPInterfaceDescription &IFace)
@@ -250,12 +243,12 @@ FString WingUtils::FormatName(const FBPInterfaceDescription &IFace)
FString WingUtils::FormatName(const UWingComponentReference *Ref) FString WingUtils::FormatName(const UWingComponentReference *Ref)
{ {
return SanitizeName(Ref->VariableName); return ExternalizeID(Ref->VariableName);
} }
FString WingUtils::FormatName(const UWidget *Widget) FString WingUtils::FormatName(const UWidget *Widget)
{ {
return SanitizeName(Widget->GetName()); return ExternalizeID(Widget->GetName());
} }
// ============================================================ // ============================================================

View File

@@ -63,7 +63,7 @@ bool WingVariables::CheckSanity(Cat Category)
TSet<FName> NamesUsed; TSet<FName> NamesUsed;
for (const Var &Variable : Variables) for (const Var &Variable : Variables)
{ {
FString VarName = WingUtils::SanitizeName(Variable.Name); FString VarName = WingUtils::ExternalizeID(Variable.Name);
FString TypeText = UWingTypes::TypeToText(Variable.Type); FString TypeText = UWingTypes::TypeToText(Variable.Type);
if (NamesUsed.Contains(Variable.Name)) if (NamesUsed.Contains(Variable.Name))
{ {
@@ -141,7 +141,7 @@ void WingVariables::PrintAll(FStringBuilderBase &Out, int32 Indent)
for (const Var& V : Variables) for (const Var& V : Variables)
{ {
FString TypeStr = UWingTypes::TypeToText(V.Type); FString TypeStr = UWingTypes::TypeToText(V.Type);
FString NameStr = WingUtils::SanitizeName(V.Name); FString NameStr = WingUtils::ExternalizeID(V.Name);
// Build flags string. // Build flags string.
FString FlagsStr; FString FlagsStr;

View File

@@ -47,10 +47,8 @@ public:
// //
// The goal here is to centralize the code that outputs // The goal here is to centralize the code that outputs
// names, and have everybody use it, so that names are // names, and have everybody use it, so that names are
// used consistently. The secondary goal is to choose // used consistently. When an identifier is output by
// names that are as uniquely-identifying as is practical. // FormatName, it's always an ExternalID.
// It's not always 100% possible to get perfectly unique
// names, though, so our code needs to check for ambiguity.
// //
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
@@ -92,21 +90,12 @@ public:
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
template<typename T> template<typename T>
static bool Identifies(const FString &Name, T&& Obj) static bool Identifies(const FString &ExternalID, T&& Obj)
{ {
return FormatName(std::forward<T>(Obj)).Equals(Name, ESearchCase::IgnoreCase); return FormatName(std::forward<T>(Obj)).Equals(ExternalID, ESearchCase::IgnoreCase);
} }
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
//
template<typename T>
static TArray<FString> FormatNames(const TArray<T> &Array)
{
TArray<FString> Result;
for (T& Elt : Array) Result.Add(FormatName(Elt));
return Result;
}
template<typename T> template<typename T>
static TArray<T*> FindAllNamed(const FString &Name, const TArray<T*> &Array) static TArray<T*> FindAllNamed(const FString &Name, const TArray<T*> &Array)
@@ -155,16 +144,8 @@ public:
} }
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
static FString ExternalizeID(const FString& Name);
//////////////////////////////////////////////////////// static FString ExternalizeID(FName Name);
// Our parsers reserve certain punctuation marks for parsing
// types, paths, and the like. For example: Array<Int>.
// We therefore cannot allow those specific punctuation marks
// in names. We replace them with similar-looking unicode
// characters.
////////////////////////////////////////////////////////
static FString SanitizeName(const FString& Name);
static FString SanitizeName(FName Name);
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// In Unreal, Menu items tend to be an unpredictable // In Unreal, Menu items tend to be an unpredictable
@@ -178,14 +159,13 @@ public:
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// This routine is used when the LLM is proposing a new // This routine is used when the LLM is proposing a new
// name in order to create a new graph, new node, or // name in order to create a new graph, new node, or
// something like that. This verifies that the name doesn't // something like that. This verifies that the name is
// contain weird control characters that will confuse // a valid external ID, it converts it to an internal ID,
// our name sanitization routines. If the name is valid, // and it also verifies that it's a readable internal ID.
// returns the *internal* name which should be stored in // If anything goes wrong, it prints an error and returns
// unreal's data structures. This is different than the // empty string.
// name used by the LLM. If not valid, returns empty
// string.
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
[[nodiscard]] static FString CheckProposedName(const FString &Name); [[nodiscard]] static FString CheckProposedName(const FString &Name);
static FString FormatNodeTitle(const UEdGraphNode *Node); static FString FormatNodeTitle(const UEdGraphNode *Node);
@@ -240,8 +220,5 @@ public:
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name);
static bool CheckCanRename(UEdGraphNode* Node, const FString &Name); static bool CheckCanRename(UEdGraphNode* Node, const FString &Name);
private:
static FString UnsanitizeName(const FString &Input);
}; };

0
tools/wing-rename.py Normal file → Executable file
View File