From 34011e43d514af75da486eecdf083f8c3b2f2c48 Mon Sep 17 00:00:00 2001 From: jyelon Date: Mon, 13 Apr 2026 21:33:02 -0400 Subject: [PATCH] Overhaul of manual-generation code --- .../UEWingman/Handlers/Documentation_Manual.h | 22 +- .../Source/UEWingman/Private/WingFetcher.cpp | 16 +- .../Source/UEWingman/Private/WingManual.cpp | 375 +++++++----------- .../Source/UEWingman/Private/WingServer.cpp | 13 +- .../Source/UEWingman/Private/WingTypes.cpp | 18 +- .../Source/UEWingman/Private/WingUtils.cpp | 4 +- .../Source/UEWingman/Public/WingManual.h | 43 +- .../Source/UEWingman/Public/WingServer.h | 8 +- 8 files changed, 235 insertions(+), 264 deletions(-) diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Documentation_Manual.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Documentation_Manual.h index 7ce38f76..3e3dbe3c 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Documentation_Manual.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Documentation_Manual.h @@ -12,6 +12,9 @@ class UWing_Documentation_Manual : public UWingHandler GENERATED_BODY() public: + UPROPERTY(EditAnywhere, meta=(Optional, Description="If specified, print only this section of the manual")) + FString Section; + virtual void Register() override { UWingServer::AddHandler(this, @@ -19,6 +22,23 @@ public: } virtual void Handle() override { - WingManual::PrintManual({WingManual::Section::All}, nullptr, false); + if (Section.IsEmpty()) + { + UWingManualSections::FetcherPaths(); + UWingManualSections::ExpressingTypes(); + UWingManualSections::VariableDeclarations(); + UWingManualSections::EscapeSequencesInFNames(); + UWingManualSections::MaterialEditing(); + UWingManualSections::ImportantCommands(); + } + else + { + FName SectionName(*Section); + if (!WingManual::PrintSection(SectionName)) + { + WingOut::Stdout.Printf(TEXT("ERROR: Unknown manual section '%s'\nAvailable sections: "), *Section); + WingManual::PrintSectionNames(WingManual::GetSections()); + } + } } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp index 8c91a30d..63d295f8 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp @@ -53,7 +53,7 @@ void WingFetcher::PathFailed(const TCHAR* Expected) Errors.Printf(TEXT("ERROR: Path specifies a %s, but expected %s\n"), *Obj.Get()->GetClass()->GetName(), Expected); else Errors.Printf(TEXT("ERROR: Path led to a null pointer\n")); - UWingServer::SuggestManual(WingManual::Section::Paths); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); SetError(); } @@ -63,7 +63,7 @@ WingFetcher& WingFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expecte Errors.Printf(TEXT("ERROR: Input to '%s' is %s, but expected %s\n"), Walker, *Obj.Get()->GetClass()->GetName(), Expected); else Errors.Printf(TEXT("ERROR: Path led to a null pointer\n")); - UWingServer::SuggestManual(WingManual::Section::Paths); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); SetError(); return *this; } @@ -75,8 +75,8 @@ WingFetcher& WingFetcher::Walk(const FString& Path) if (Path.Contains(TEXT(" "))) { Errors.Printf(TEXT("ERROR: Paths may not contain whitespace.")); - UWingServer::SuggestManual(WingManual::Section::Paths); - UWingServer::SuggestManual(WingManual::Section::EscapeSequences); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, EscapeSequencesInFNames)); return SetError(); } TArray Segments; @@ -84,7 +84,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path) if (Segments.Num() == 0) { Errors.Print(TEXT("ERROR: Empty path\n")); - UWingServer::SuggestManual(WingManual::Section::Paths); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); return SetError(); } @@ -105,7 +105,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path) if (!Func) { Errors.Printf(TEXT("ERROR: Unknown path step '%s'\n"), *Key); - UWingServer::SuggestManual(WingManual::Section::Paths); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); return SetError(); } (this->*Func)(Value); @@ -122,7 +122,7 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath) if (!PackagePath.StartsWith(TEXT("/"))) { Errors.Printf(TEXT("ERROR: Path must start with '/', got '%s'\n"), *PackagePath); - UWingServer::SuggestManual(WingManual::Section::Paths); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); return SetError(); } @@ -182,7 +182,7 @@ WingFetcher& WingFetcher::Graph(const FString& Value) if (!Value.IsEmpty()) { Errors.Printf(TEXT("ERROR: Materials have only one graph, with a blank name.\n\n")); - UWingServer::SuggestManual(WingManual::Section::Paths); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, FetcherPaths)); return SetError(); } WingUtils::EnsureMaterialGraph(Mat); diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingManual.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingManual.cpp index 582cf630..abdc3e09 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingManual.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingManual.cpp @@ -58,241 +58,168 @@ void WingManual::PrintHandlerHelp(const FWingHandlerConfig& Handler) WingOut::Stdout.Print(TEXT("\n")); } -void WingManual::PrintManual(TSet
Sections, const FWingHandlerConfig* Handler, bool Abridged) +void UWingManualSections::FetcherPaths() { - if (Sections.IsEmpty()) return; + WingOut::Stdout.Print(TEXT( + "\n FETCHER PATHS:" + "\n" + "\n Most commands require you to specify a 'fetcher path'." + "\n A fetcher path starts with an asset name, followed by" + "\n steps that navigate into the asset. Some Examples:" + "\n" + "\n /Game/Widgets/WB_Hotkeys,widget:Canvas.122" + "\n /Game/Testing/BP_Test,graph:Rescale.Actor,node:K2Node_CallFunction_0,pin:Scale" + "\n /Game/Chars/BP_Manny,component:Camera.Boom" + "\n" + "\n The navigation steps supported are:" + "\n" + "\n graph — move from a blueprint or material to a graph." + "\n node — move from a graph to a graph node" + "\n pin — move from a graph node to a pin" + "\n component — move from a blueprint to a component" + "\n levelblueprint — move from a world to a blueprint" + "\n widget — move from a widget blueprint to a widget" + "\n structprop — move into a struct property of an object" + "\n" + "\n Notice that paths use escaped fnames. See the section" + "\n on escape sequences in fnames below sfor more information." + "\n" + "\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" + )); +} - const bool bPrintAll = Sections.Contains(Section::All); +void UWingManualSections::ExpressingTypes() +{ + WingOut::Stdout.Print(TEXT( + "\n EXPRESSING TYPES:" + "\n" + "\n To change the type of a variable, or to express function parameters," + "\n you will use our syntax for types. Here are some valid examples:" + "\n" + "\n Bool, String, Vector, Rotator, HitResult, Actor, Character," + "\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn," + "\n Array, Set, Map" + "\n Soft, Class, SoftClass" + "\n" + "\n Notice that it's 'Actor', not 'AActor'. Type names are not" + "\n case-sensitive. When a blueprint like /Game/Testing/BP_Foo" + "\n is used as a type, the typename is just BP_Foo. You can search" + "\n for valid types using the TypeName_Search command." + "\n" + )); +} - if (Abridged) - { - WingOut::Stdout.Printf(TEXT("\n--- AUTOMATIC DOCUMENTATION ---\n")); - } +void UWingManualSections::VariableDeclarations() +{ + WingOut::Stdout.Print(TEXT( + "\n VARIABLE DECLARATIONS:" + "\n" + "\n We have our own syntax for variable declarations: a type," + "\n a name, optional flags, and an optional default value," + "\n always on one line:" + "\n" + "\n Array Actors" + "\n Float F (InstanceEditable)" + "\n String S = This is the default value" + "\n" + "\n The commands Variables_Add, Variables_Modify," + "\n and Variables_Remove can be used to edit " + "\n blueprint variables, graph local variables, graph input" + "\n variables, graph output variables, and custom" + "\n event node input variables. Event dispatchers are" + "\n also graphs, so they too can be edited." + "\n" + )); +} - if (Handler && (Sections.Contains(Section::HandlerHelp) || bPrintAll)) - { - PrintHandlerHelp(*Handler); - } +void UWingManualSections::EscapeSequencesInFNames() +{ + WingOut::Stdout.Print(TEXT( + "\n ESCAPE SEQUENCES IN FNAMES:" + "\n" + "\n When we output FNames, we use HTML escape sequences for the" + "\n following marks: \\\"'(),.:;<=>&, and for certain other characters." + "\n We also translate spaces to periods." + "\n" + "\n When sending FNames to UE Wingman, you *must* escape the marks" + "\n listed above, but you *may* escape any character. To send an FName" + "\n with a space in it, either use or a period." + "\n" + )); +} - if (Sections.Contains(Section::Paths) || bPrintAll) +void UWingManualSections::MaterialEditing() +{ + WingOut::Stdout.Print(TEXT( + "\n MATERIAL EDITING:" + "\n" + "\n We do not expose material expressions directly. Instead, you" + "\n will be editing the material graph. However, if you Graph_Dump" + "\n a material graph, you will see that the nodes contain" + "\n properties which actually come from the material expressions." + "\n You can edit these using Details_Set on the node." + "\n" + "\n Don't overlook custom HLSL nodes. These can accomplish in\n" + "\n a single node what would otherwise take many.\n" + "\n" + )); +} + +void UWingManualSections::ImportantCommands() +{ + WingOut::Stdout.Print(TEXT( + "\n IMPORTANT COMMANDS:" + "\n" + "\n Documentation_Manual: print manual sections" + "\n Documentation_Commands: a list of all the main commands" + "\n Documentation_CreateAssets: Additional commands that create new assets" + "\n Blueprint_Dump: a summary of any blueprint" + "\n Graph_Dump: a fairly detailed listing of any Graph" + "\n Details_Dump: Dump the details panel for a given object" + "\n Details_Set: Manipulate the details panel for a given object" + "\n" + "\n You can use Documentation_Commands(Query=Command,Verbose=true)" + "\n to get detailed help for a specific command." + "\n" + "\n You can use Documentation_Manual(Section=Name) to print out" + "\n a specific manual section." + "\n" + )); +} + +TSet WingManual::GetSections() +{ + TSet Result; + for (TFieldIterator It(UWingManualSections::StaticClass(), EFieldIterationFlags::None); It; ++It) { - if (Abridged) - { - WingOut::Stdout.Print(TEXT( - "\n PATHS: Here are some example paths:" - "\n /Game/Widgets/WB_Hotkeys,widget:Canvas·122" - "\n /Game/Testing/BP_Test,graph:Rescale·Actor,node:K2Node_CallFunction_0,pin:Scale" - "\n /Game/Chars/BP_Manny,component:Camera·Boom" - "\n" - )); - } - else - { - WingOut::Stdout.Print(TEXT( - "\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 that navigate into the asset. Some Examples:" - "\n" - "\n /Game/Widgets/WB_Hotkeys,widget:Canvas.122" - "\n /Game/Testing/BP_Test,graph:Rescale.Actor,node:K2Node_CallFunction_0,pin:Scale" - "\n /Game/Chars/BP_Manny,component:Camera.Boom" - "\n" - "\n The navigation steps supported are:" - "\n" - "\n graph — move from a blueprint or material to a graph." - "\n node — move from a graph to a graph node" - "\n pin — move from a graph node to a pin" - "\n component — move from a blueprint to a component" - "\n levelblueprint — move from a world to a blueprint" - "\n widget — move from a widget blueprint to a widget" - "\n" - "\n Notice that paths use sanitized identifiers. See the section" - "\n on identifier sanitization below for more information." - "\n" - "\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" - )); - } + Result.Add(It->GetFName()); } + return Result; +} - if (Sections.Contains(Section::Types) || bPrintAll) +void WingManual::PrintSectionNames(const TSet& Sections) +{ + if (Sections.IsEmpty()) return; + bool bFirst = true; + for (const FName& Section : Sections) { - if (Abridged) - { - WingOut::Stdout.Print(TEXT( - "\n TYPES: Here are some examples of valid types:" - "\n Bool, String, Vector, Rotator, HitResult, Actor, Character," - "\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn," - "\n Array, Set, Map" - "\n Soft, Class, SoftClass" - "\n" - )); - } - else - { - WingOut::Stdout.Print(TEXT( - "\n TYPES:" - "\n" - "\n To change variable types, or to express function prototypes, you will" - "\n use our syntax for types. Here are some valid examples:" - "\n" - "\n Bool, String, Vector, Rotator, HitResult, Actor, Character," - "\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn," - "\n Array, Set, Map" - "\n Soft, Class, SoftClass" - "\n" - "\n Notice that it's 'Actor', not 'AActor'. Type names are not" - "\n case-sensitive. When a blueprint like /Game/Testing/BP_Foo" - "\n is used as a type, the typename is BP_Foo." - "\n" - )); - } + if (!bFirst) WingOut::Stdout.Print(TEXT(", ")); + bFirst = false; + WingOut::Stdout.Printf(TEXT("%s"), *Section.ToString()); } + WingOut::Stdout.Print(TEXT("\n")); +} - if (Sections.Contains(Section::VariableDeclarations) || bPrintAll) - { - if (Abridged) - { - WingOut::Stdout.Print(TEXT( - "\n VARIABLE DECLARATIONS: example variable declarations:" - "\n Array Actors" - "\n Float F (InstanceEditable)" - "\n String S = This is the default value" - )); - } - else - { - WingOut::Stdout.Print(TEXT( - "\n VARIABLE DECLARATIONS:" - "\n" - "\n We have our own syntax for variable declarations: a type," - "\n a name, optional flags, and an optional default value," - "\n always on one line:" - "\n" - "\n Array Actors" - "\n Float F (InstanceEditable)" - "\n String S = This is the default value" - "\n" - "\n The commands Variables_Add, Variables_Modify," - "\n and Variables_Remove can be used to edit: " - "\n blueprint variables, graph local variables, graph input" - "\n variables, graph output variables, and custom" - "\n event node input variables. Event dispatchers are" - "\n also graphs, so they too can be edited." - "\n" - )); - } - } - - if (Sections.Contains(Section::EscapeSequences) || bPrintAll) - { - if (Abridged) - { - WingOut::Stdout.Print(TEXT( - "\n USING HTML ESCAPE SEQUENCES:" - "\n When we output FNames, we use HTML escape sequences for the" - "\n following marks: \\\"'(),.:;<=>& We also escape control" - "\n characters, and some (but not all) unicode characters." - "\n We also translate spaces to periods. Examples:" - "\n" - "\n FName(TEXT(\"No_Change\")) --> No_Change" - "\n FName(TEXT(\"ίδιος\")) --> ίδιος" - "\n FName(TEXT(\"✡✢❄\")) --> ✡✢❄" - "\n FName(TEXT(\"Hello.There\")) --> Hello.There" - "\n FName(TEXT(\"Hello There\")) --> Hello.There" - "\n FName(TEXT(\"Hello\n\")) --> Hello " - "\n " - "\n When sending FNames to UE Wingman, you *must* escape the marks" - "\n listed above and control characters, but you *may* escape" - "\n any character. To send an FName with a space in it, either" - "\n use or a period." - "\n" - "\n Currently, this escaping *only* applies to FNames. It" - "\n doesn't work to use escapes in asset names or file names." - "\n" - )); - } - else - { - WingOut::Stdout.Print(TEXT( - "\n USING HTML ESCAPE SEQUENCES:" - "\n When we output FNames, we use HTML escape sequences for the" - "\n following marks: \\\"'(),.:;<=>&, and for certain other characters." - "\n We also translate spaces to periods." - "\n" - "\n When sending FNames to UE Wingman, you *must* escape the marks" - "\n listed above, but you *may* escape any character. To send an FName" - "\n with a space in it, either use or a period." - "\n" - )); - } - } - - if (Sections.Contains(Section::Whitespace) || bPrintAll) - { - WingOut::Stdout.Print(TEXT( - "\n ABOUT WHITESPACE:" - "\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" - )); - } - - if (Sections.Contains(Section::MaterialEditing) || bPrintAll) - { - if (Abridged) - { - WingOut::Stdout.Print(TEXT( - "\n MATERIAL EDITING:" - "\n We do not expose material expressions directly. Instead, use" - "\n Details_Dump and Details_Set on the material graph nodes to" - "\n edit material expression properties." - "\n" - )); - } - else - { - WingOut::Stdout.Print(TEXT( - "\n MATERIAL EDITING:" - "\n" - "\n We do not expose material expressions directly. Instead, you" - "\n will be editing the material graph. However, if you Graph_Dump" - "\n a material graph, you will see that the nodes contain" - "\n properties which actually come from the material expressions." - "\n You can edit these using Details_Set on the node." - "\n" - )); - } - } - - if (Sections.Contains(Section::ImportantCommands) || bPrintAll) - { - WingOut::Stdout.Print(TEXT( - "\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:" - "\n Documentation_Manual: this explanation" - "\n Documentation_Commands: a list of all the main commands" - "\n Documentation_CreateAssets: Additional commands that create new assets" - "\n Blueprint_Dump: a summary of any blueprint" - "\n Graph_Dump: a fairly detailed listing of any Graph" - "\n Details_Dump: Dump the details panel for a given object" - "\n Details_Set: Manipulate the details panel for a given object" - "\n" - "\n You can use Documentation_Commands(Query=SomeCommand,Verbose=true)" - "\n to get detailed help for a specific command." - "\n" - )); - } - - if (Abridged) - { - WingOut::Stdout.Printf(TEXT("\nUse command 'Documentation_Manual' to see the full manual.\n")); - } +bool WingManual::PrintSection(FName Section) +{ + UFunction* Func = UWingManualSections::StaticClass()->FindFunctionByName(Section); + if (!Func) return false; + UWingManualSections::StaticClass()->GetDefaultObject()->ProcessEvent(Func, nullptr); + return true; } void WingManual::Commands(EWingHandlerKind Kind, const FString& Query, bool Verbose) diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp index 3a49a027..e54f794f 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp @@ -175,6 +175,7 @@ FString UWingServer::HandleRequest(const FString& Line) LogCapture.bEnabled = true; WingOut::StdoutBuffer.Reset(); SuggestedManualSections.Empty(); + bSuggestHandlerHelp = false; LastHandler = nullptr; TryCallHandler(Line); @@ -186,10 +187,14 @@ FString UWingServer::HandleRequest(const FString& Line) WingOut::Stdout.Printf(TEXT("UE_LOG: %s\n"), *Msg); } LogCapture.CapturedErrors.Empty(); + if (bSuggestHandlerHelp && LastHandler) + { + WingManual::PrintHandlerHelp(*LastHandler); + } if (!SuggestedManualSections.IsEmpty()) { - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); - WingManual::PrintManual(SuggestedManualSections, LastHandler, true); + WingOut::Stdout.Print(TEXT("Suggested manual sections: ")); + WingManual::PrintSectionNames(SuggestedManualSections); } FString Result = WingOut::StdoutBuffer.ToString(); WingOut::StdoutBuffer.Reset(); @@ -227,7 +232,7 @@ void UWingServer::TryCallHandler(const FString &Line) if (!Found) { WingOut::Stdout.Printf(TEXT("Unknown command: %s"), *Command); - UWingServer::SuggestManual(WingManual::Section::ImportantCommands); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, ImportantCommands)); return; } LastHandler = Found; @@ -241,7 +246,7 @@ void UWingServer::TryCallHandler(const FString &Line) TArray Props = FWingProperty::GetVisible(Handler); if (!FWingProperty::PopulateFromJson(Props, *Request, false, WingOut::Stdout)) { - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); return; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp index 57964f46..4a929847 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingTypes.cpp @@ -430,7 +430,7 @@ void UWingTypes::PrintParseError(WingTokenizer& Tok, const TCHAR* Message, WingO { FString TypeText(Tok.GetRange(NAME_StartOfType, 1)); Errors.Printf(TEXT("ERROR parsing type '%s' — %s\n"), *TypeText, Message); - UWingServer::SuggestManual(WingManual::Section::Types); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, ExpressingTypes)); } @@ -626,7 +626,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con if (IsNone && OutPinType.IsContainer()) { Errors.Printf(TEXT("ERROR: 'None' is not allowed in an array/set/map\n")); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } @@ -634,7 +634,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con if (!Require.AllowNone && IsNone) { Errors.Printf(TEXT("ERROR: 'None' is not allowed here\n")); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } @@ -644,7 +644,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con { FString Text(Tok.GetRange(NAME_StartOfType, 0)); Errors.Printf(TEXT("ERROR: Type '%s' is a container, not allowed here\n"), *Text); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } } @@ -655,7 +655,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con { Errors.Printf(TEXT("ERROR: Need a type which is an %s, got a %s instead.\n"), *Require.PinCategory.ToString(), *OutPinType.PinCategory.ToString()); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } } @@ -668,13 +668,13 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con { FString Text(Tok.GetRange(NAME_StartOfType, 0)); Errors.Printf(TEXT("ERROR: '%s' is an interface, not a class\n"), *Text); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } if (!IsChildOf(OutPinType, Require.IsChildOf)) { Errors.Printf(TEXT("ERROR: Type must derive from %s\n"), *WingUtils::FormatName(Require.IsChildOf)); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } } @@ -685,7 +685,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con { FString Text(Tok.GetRange(NAME_StartOfType, 0)); Errors.Printf(TEXT("ERROR: Not a blueprint type: %s\n"), *Text); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } } @@ -696,7 +696,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con { FString Text(Tok.GetRange(NAME_StartOfType, 0)); Errors.Printf(TEXT("ERROR: Not a blueprintable type: %s\n"), *Text); - UWingServer::SuggestManual(WingManual::Section::HandlerHelp); + UWingServer::SuggestHandlerHelp(); OutPinType = FEdGraphPinType(); return false; } } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp index ce41638e..0caa8060 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingUtils.cpp @@ -60,7 +60,7 @@ FName WingUtils::CheckInternalizeID(const FString &ExternalID, WingOut Errors) if (!Error.IsEmpty()) { Errors.Printf(TEXT("%s\n"), *Error); - UWingServer::SuggestManual(WingManual::Section::EscapeSequences); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, EscapeSequencesInFNames)); } return InternalID; } @@ -77,7 +77,7 @@ FName WingUtils::CheckProposedName(const FString &ExternalID, const TSet { Errors.Printf(TEXT("ERROR: id %s would not be a readable id, may not create item with this name"), *ExternalID); - UWingServer::SuggestManual(WingManual::Section::EscapeSequences); + UWingServer::SuggestManual(GET_FUNCTION_NAME_CHECKED(UWingManualSections, EscapeSequencesInFNames)); return FName(); } if (InUse.Contains(InternalID)) diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingManual.h b/Plugins/UEWingman/Source/UEWingman/Public/WingManual.h index a878eaf7..26dfed5a 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingManual.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingManual.h @@ -1,5 +1,6 @@ #pragma once #include "CoreMinimal.h" +#include "WingManual.generated.h" struct FWingHandlerConfig; enum class EWingHandlerKind; @@ -7,23 +8,37 @@ enum class EWingHandlerKind; class WingManual { public: - enum class Section - { - All, - HandlerHelp, - Paths, - Types, - VariableDeclarations, - EscapeSequences, - Whitespace, - MaterialEditing, - ImportantCommands, - }; - static void PrintHandlerPrototype(const FWingHandlerConfig& Handler); static void PrintHandlerArguments(const FWingHandlerConfig& Handler); static void PrintHandlerDescription(const FWingHandlerConfig& Handler); static void PrintHandlerHelp(const FWingHandlerConfig& Handler); - static void PrintManual(TSet
Sections, const FWingHandlerConfig* Handler, bool Abridged); static void Commands(EWingHandlerKind Kind, const FString& Query, bool Verbose); + static TSet GetSections(); + static bool PrintSection(FName Section); + static void PrintSectionNames(const TSet& Sections); +}; + +UCLASS() +class UWingManualSections : public UObject +{ + GENERATED_BODY() + +public: + UFUNCTION() + static void FetcherPaths(); + + UFUNCTION() + static void ExpressingTypes(); + + UFUNCTION() + static void VariableDeclarations(); + + UFUNCTION() + static void EscapeSequencesInFNames(); + + UFUNCTION() + static void MaterialEditing(); + + UFUNCTION() + static void ImportantCommands(); }; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h b/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h index 1978b007..34169251 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h @@ -48,7 +48,10 @@ public: static void AddTouchedObject(UObject* Obj) { GWingServer->Notifier.AddTouchedObject(Obj); } /** Suggest that a manual section be printed after the handler finishes. */ - static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); } + static void SuggestManual(FName Section) { GWingServer->SuggestedManualSections.Add(Section); } + + /** Suggest that handler help be printed after the handler finishes. */ + static void SuggestHandlerHelp() { GWingServer->bSuggestHandlerHelp = true; } /** Port the server is listening on. */ int32 GetPort() const { return Port; } @@ -64,7 +67,8 @@ private: UPROPERTY() FWingNotifier Notifier; FWingHandlerConfig* LastHandler; - TSet SuggestedManualSections; + bool bSuggestHandlerHelp; + TSet SuggestedManualSections; FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request TArray WingHandlerRegistry; // sorted by Name void BuildWingHandlerRegistry();