Change syntax back to normal

This commit is contained in:
2026-03-19 10:16:44 -04:00
parent cc3d03541c
commit d6cc090aca
9 changed files with 85 additions and 60 deletions

View File

@@ -17,7 +17,7 @@ public:
UPROPERTY(meta=(Description="Path to a graph node (function entry, function result, custom event, or tunnel)"))
FString Node;
UPROPERTY(meta=(Description="Args e.g. 'int◦x│floaty'"))
UPROPERTY(meta=(Description="Args e.g. 'int x,float y'"))
FString Args;
virtual FString GetDescription() const override

View File

@@ -22,10 +22,10 @@ public:
"\n PATHS:"
"\n"
"\n Most commands require you to specify a path. A path starts"
"\n with an asset name, followed by steps separated by "
"\n with an asset name, followed by steps separated by ,"
"\n that navigate into the asset. Example:"
"\n"
"\n /Game/Widgets/WB_Hotkeysgraph:EventGraphnode:Self03pin:Result"
"\n /Game/Widgets/WB_Hotkeys,graph:EventGraph,node:Self03,pin:Result"
"\n"
"\n The navigation steps supported are:"
"\n"
@@ -38,7 +38,7 @@ public:
"\n Steps do not always require a parameter. For example, materials"
"\n only have one graph, so you can just say:"
"\n"
"\n /Game/Materials/MyMaterialgraph"
"\n /Game/Materials/MyMaterial,graph"
"\n"
"\n TYPES:"
"\n"
@@ -53,15 +53,15 @@ public:
"\n Notice that it's 'actor', not 'AActor'."
"\n You can use the following notations for complex types:"
"\n"
"\n Softabp_manny, Classpawn, SoftClasspawn"
"\n Arrayint, Setstring, Mapintstring"
"\n Soft<abp_manny>, Class<pawn>, SoftClass<pawn>"
"\n Array<int>, Set<string>, Map<int,string>"
"\n"
"\n FUNCTION ARGUMENTS AND RETURN VALUES:"
"\n"
"\n Function argument lists are expressed as -separated"
"\n lists of typename pairs:"
"\n Function argument lists are expressed as comma-separated"
"\n lists of type-name pairs:"
"\n"
"\n double◦D│PlayerController◦P│Arrayint▸◦A"
"\n double D,PlayerController P,Array<int> A"
"\n"
"\n To change the arguments or return values of a function, edit the"
"\n entry or exit node of the graph using GraphNode_SetArgs."
@@ -70,13 +70,11 @@ public:
"\n before you can set return values. Custom event nodes also have"
"\n editable arguments."
"\n"
"\n ABOUT UNICODE AND WHITESPACE:"
"\n ABOUT WHITESPACE:"
"\n"
"\n We use unicode geometric shapes as delimiters in"
"\n some places, such as in typenames, function arguments, and"
"\n paths (see above). This is intentional, you really"
"\n do have to use those delimiters. Do not put excess"
"\n whitespace into paths or typenames."
"\n Do not put excess whitespace into paths, typenames, or"
"\n function prototypes, only use whitespace where it is required"
"\n by the syntax."
"\n"
"\n MATERIAL EDITING:"
"\n"

View File

@@ -77,7 +77,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
if (bError) return *this;
TArray<FString> Segments;
Path.ParseIntoArray(Segments, TEXT(""));
Path.ParseIntoArray(Segments, TEXT(","));
if (Segments.Num() == 0)
{
UWingServer::Print(TEXT("ERROR: Empty path\n"));
@@ -164,7 +164,7 @@ WingFetcher& WingFetcher::Graph(const FString& Value)
{
if (bError) return *this;
// Material with blank graph name → navigate to the material graph.
// Material with blank graph name
if (UMaterial* Mat = ::Cast<UMaterial>(Obj))
{
if (!Value.IsEmpty())

View File

@@ -3,6 +3,7 @@
#include "K2Node_FunctionResult.h"
#include "K2Node_Tunnel.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "WingServer.h"
bool WingFunctionArgs::HasArgs(UEdGraphNode* Node)
@@ -20,8 +21,8 @@ FString WingFunctionArgs::GetArgs(UEdGraphNode* Node)
TStringBuilder<256> SB;
for (const TSharedPtr<FUserPinInfo>& Pin : Editable->UserDefinedPins)
{
if (SB.Len() > 0) SB << TEXT("");
SB << UWingTypes::TypeToText(Pin->PinType) << TEXT("") << Pin->PinName.ToString();
if (SB.Len() > 0) SB << TEXT(",");
SB << UWingTypes::TypeToText(Pin->PinType) << TEXT(" ") << WingUtils::FormatName(*Pin);
}
return FString(SB);
}
@@ -42,18 +43,18 @@ bool WingFunctionArgs::ParseArgs(const FString& Args, TArray<FParsedArg>& OutArg
if (Trimmed.IsEmpty()) return true;
TArray<FString> Parts;
Trimmed.ParseIntoArray(Parts, TEXT(""));
Trimmed.ParseIntoArray(Parts, TEXT(","));
for (const FString& Part : Parts)
{
FString Token = Part.TrimStartAndEnd();
if (Token.IsEmpty()) continue;
// Split "typename" on the white bullet.
// Split "type name"
FString TypeStr, NameStr;
if (!Token.Split(TEXT(""), &TypeStr, &NameStr))
if (!Token.Split(TEXT(" "), &TypeStr, &NameStr))
{
UWingServer::Printf(TEXT("ERROR: Expected 'typename' but got '%s'\n"), *Token);
UWingServer::Printf(TEXT("ERROR: Expected 'type name' but got '%s'\n"), *Token);
return false;
}

View File

@@ -104,13 +104,13 @@ void UWingTypes::ChooseShortName(const FAssetData &Data)
{
// Blueprint: the generated class is AssetName_C
FString ObjectPath = FString::Printf(TEXT("%s.%s_C"), *PackageName, *AssetName);
ChooseShortName(AssetName, ObjectPath);
ChooseShortName(WingUtils::SanitizeName(AssetName), ObjectPath);
}
else if (ClassPath == UUserDefinedStruct::StaticClass()->GetClassPathName() ||
ClassPath == UUserDefinedEnum::StaticClass()->GetClassPathName())
{
FString ObjectPath = FString::Printf(TEXT("%s.%s"), *PackageName, *AssetName);
ChooseShortName(AssetName, ObjectPath);
ChooseShortName(WingUtils::SanitizeName(AssetName), ObjectPath);
}
}
@@ -159,11 +159,11 @@ FString UWingTypes::TypeToTextInner(FName Category, FName SubCategory, UObject*
if (Category == UEdGraphSchema_K2::PC_Object)
return Short;
if (Category == UEdGraphSchema_K2::PC_Class)
return FString::Printf(TEXT("Class%s"), *Short);
return FString::Printf(TEXT("Class<%s>"), *Short);
if (Category == UEdGraphSchema_K2::PC_SoftObject)
return FString::Printf(TEXT("Soft%s"), *Short);
return FString::Printf(TEXT("Soft<%s>"), *Short);
if (Category == UEdGraphSchema_K2::PC_SoftClass)
return FString::Printf(TEXT("SoftClass%s"), *Short);
return FString::Printf(TEXT("SoftClass<%s>"), *Short);
if (Category == UEdGraphSchema_K2::PC_Interface)
return Short;
}
@@ -181,9 +181,9 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType)
return FString();
if (PinType.IsArray())
return FString::Printf(TEXT("Array%s"), *Inner);
return FString::Printf(TEXT("Array<%s>"), *Inner);
if (PinType.IsSet())
return FString::Printf(TEXT("Set%s"), *Inner);
return FString::Printf(TEXT("Set<%s>"), *Inner);
if (PinType.IsMap())
{
FString ValueInner = Types->TypeToTextInner(
@@ -192,7 +192,7 @@ FString UWingTypes::TypeToText(const FEdGraphPinType& PinType)
PinType.PinValueType.TerminalSubCategoryObject.Get());
if (ValueInner.IsEmpty())
return FString();
return FString::Printf(TEXT("Map%s%s"), *Inner, *ValueInner);
return FString::Printf(TEXT("Map<%s,%s>"), *Inner, *ValueInner);
}
return Inner;
@@ -447,29 +447,29 @@ bool UWingTypes::ParsePlainIdentifier(FEdGraphPinType& OutType)
bool UWingTypes::ParseWrapped(FName Wrapper, FEdGraphPinType& OutType)
{
Cursor++;
if (!ParseChar(L'')) return false;
if (!ParseChar('<')) return false;
if (!ParsePlainIdentifier(OutType)) return false;
if (!Cast<UClass>(OutType.PinSubCategoryObject))
{
Error = FString::Printf(TEXT("%s is not a Class"), *OutType.PinSubCategoryObject->GetName());
return false;
}
if (!ParseChar(L'')) return false;
if (!ParseChar('>')) return false;
OutType.PinCategory = Wrapper;
return true;
}
bool UWingTypes::ParseMaybeWrapped(FEdGraphPinType& OutType)
{
if (TokenIs(TEXT("Soft"), L''))
if (TokenIs(TEXT("Soft"), '<'))
{
return ParseWrapped(UEdGraphSchema_K2::PC_SoftObject, OutType);
}
else if (TokenIs(TEXT("Class"), L''))
else if (TokenIs(TEXT("Class"), '<'))
{
return ParseWrapped(UEdGraphSchema_K2::PC_Class, OutType);
}
else if (TokenIs(TEXT("SoftClass"), L''))
else if (TokenIs(TEXT("SoftClass"), '<'))
{
return ParseWrapped(UEdGraphSchema_K2::PC_SoftClass, OutType);
}
@@ -479,40 +479,40 @@ bool UWingTypes::ParseMaybeWrapped(FEdGraphPinType& OutType)
bool UWingTypes::ParseArrayOrSet(FEdGraphPinType& OutType)
{
Cursor++;
if (!ParseChar(L'')) return false;
if (!ParseChar('<')) return false;
if (!ParseMaybeWrapped(OutType)) return false;
if (!ParseChar(L'')) return false;
if (!ParseChar('>')) return false;
return true;
}
bool UWingTypes::ParseMap(FEdGraphPinType& OutType)
{
Cursor++;
if (!ParseChar(L'')) return false;
if (!ParseChar('<')) return false;
if (!ParsePlainIdentifier(OutType)) return false;
if (!ParseChar(L'')) return false;
if (!ParseChar(',')) return false;
FEdGraphPinType ValueType;
if (!ParseMaybeWrapped(ValueType)) return false;
OutType.PinValueType.TerminalCategory = ValueType.PinCategory;
OutType.PinValueType.TerminalSubCategory = ValueType.PinSubCategory;
OutType.PinValueType.TerminalSubCategoryObject = ValueType.PinSubCategoryObject;
if (!ParseChar(L'')) return false;
if (!ParseChar('>')) return false;
return true;
}
bool UWingTypes::ParseType(FEdGraphPinType& OutType)
{
if (TokenIs(TEXT("Array"), L''))
if (TokenIs(TEXT("Array"), '<'))
{
OutType.ContainerType = EPinContainerType::Array;
if (!ParseArrayOrSet(OutType)) return false;
}
else if (TokenIs(TEXT("Set"), L''))
else if (TokenIs(TEXT("Set"), '<'))
{
OutType.ContainerType = EPinContainerType::Set;
if (!ParseArrayOrSet(OutType)) return false;
}
else if (TokenIs(TEXT("Map"), L''))
else if (TokenIs(TEXT("Map"), '<'))
{
OutType.ContainerType = EPinContainerType::Map;
if (!ParseMap(OutType)) return false;

View File

@@ -11,6 +11,7 @@
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphNode.h"
#include "EdGraph/EdGraphPin.h"
#include "K2Node_EditablePinBase.h"
#include "EdGraph/EdGraphSchema.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
@@ -53,14 +54,11 @@ extern int32 TrySavePackageSEH(
// ============================================================
// Name sanitization
//
// Our parsers use Unicode geometric shape delimiters that
// are unlikely to appear in names: ◂▸ for type brackets, ◆
// for map key◆value, ◦ for type◦name in prototypes, │ for
// list/path separation. If any of these characters appear
// in a name, we replace them with safe ASCII equivalents.
// One consequence of name sanitization is that we can't use
// Unreal's name-lookup routines — the LLM only sees
// sanitized names. So we do our own name lookups.
// 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.
// ============================================================
void WingUtils::SanitizeNameInPlace(FString &Name)
@@ -70,14 +68,13 @@ void WingUtils::SanitizeNameInPlace(FString &Name)
{
TCHAR c = Name[Src];
if (c < 0x20 || c == 0x7F) continue;
if (c == L'') c='<';
if (c == L'') c='>';
if (c == L'') c='*';
if (c == L'') c='.';
if (c == L'') c = '|';
if (c == ' ') c=L'·';
if (c == '<') c=L'';
if (c == '>') c=L'';
if (c == ',') c=L'·';
Name[Dst++] = c;
}
if (Dst == 0) Name[Dst++] = '_';
if (Dst == 0) Name[Dst++] = L'·';
Name.LeftInline(Dst);
}
@@ -216,6 +213,11 @@ FString WingUtils::FormatName(const FProperty *Prop)
return SanitizeName(Prop->GetName());
}
FString WingUtils::FormatName(const FUserPinInfo &Pin)
{
return SanitizeName(Pin.PinName);
}
// ============================================================
// Formatting other things
// ============================================================

View File

@@ -13,10 +13,10 @@ struct WingFunctionArgs
// CustomEvent, or Tunnel).
static bool HasArgs(UEdGraphNode* Node);
// Returns the user-defined pins as a string like "int◦x│floaty".
// Returns the user-defined pins as a string like "int x,float y".
static FString GetArgs(UEdGraphNode* Node);
// Sets the user-defined pins from a string like "int◦x│floaty".
// Sets the user-defined pins from a string like "int x,float y".
// Returns true on success.
static bool SetArgs(UEdGraphNode* Node, const FString& Args);

View File

@@ -30,6 +30,7 @@ class UEnum;
class USCS_Node;
struct FMemberReference;
struct FBPVariableDescription;
struct FUserPinInfo;
// Stateless utility functions used by MCP handlers and the MCP server.
// This is effectively a namespace — all methods are static.
class WingUtils
@@ -71,6 +72,7 @@ public:
static FString FormatName(const UScriptStruct *Struct);
static FString FormatName(const UEnum *Enum);
static FString FormatName(const FProperty *Prop);
static FString FormatName(const FUserPinInfo &Pin);
////////////////////////////////////////////////////////
//