Change syntax back to normal
This commit is contained in:
@@ -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│float◦y'"))
|
||||
UPROPERTY(meta=(Description="Args e.g. 'int x,float y'"))
|
||||
FString Args;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
|
||||
@@ -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_Hotkeys│graph:EventGraph│node:Self03│pin: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/MyMaterial│graph"
|
||||
"\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 Soft◂abp_manny▸, Class◂pawn▸, SoftClass◂pawn▸"
|
||||
"\n Array◂int▸, Set◂string▸, Map◂int◆string▸"
|
||||
"\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 type◦name pairs:"
|
||||
"\n Function argument lists are expressed as comma-separated"
|
||||
"\n lists of type-name pairs:"
|
||||
"\n"
|
||||
"\n double◦D│PlayerController◦P│Array◂int▸◦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"
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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 "type◦name" 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 'type◦name' but got '%s'\n"), *Token);
|
||||
UWingServer::Printf(TEXT("ERROR: Expected 'type name' but got '%s'\n"), *Token);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
// ============================================================
|
||||
|
||||
@@ -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│float◦y".
|
||||
// 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│float◦y".
|
||||
// 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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
22
find_nonascii.py
Normal file
22
find_nonascii.py
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Search UEWingman source files for non-ASCII characters."""
|
||||
|
||||
import os
|
||||
import glob
|
||||
|
||||
source_dir = "Plugins/UEWingman/Source"
|
||||
patterns = ["**/*.cpp", "**/*.h"]
|
||||
found_any = False
|
||||
|
||||
for pattern in patterns:
|
||||
for filepath in sorted(glob.glob(os.path.join(source_dir, pattern), recursive=True)):
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
for lineno, line in enumerate(f, 1):
|
||||
for col, ch in enumerate(line, 1):
|
||||
if ord(ch) > 127:
|
||||
if not found_any:
|
||||
found_any = True
|
||||
print(f"{filepath}:{lineno}:{col} U+{ord(ch):04X} {ch!r} ...{line[max(0,col-10):col+10].rstrip()}...")
|
||||
|
||||
if not found_any:
|
||||
print("No non-ASCII characters found.")
|
||||
Reference in New Issue
Block a user