// Copyright 2020 Phyronnaz #include "VoxelGraphErrorReporter.h" #include "VoxelNode.h" #include "VoxelGraphGenerator.h" #include "IVoxelGraphEditor.h" #include "VoxelNodes/VoxelGraphMacro.h" #include "EdGraph/EdGraph.h" #include "EdGraph/EdGraphNode.h" #include "Modules/ModuleManager.h" FVoxelGraphErrorReporter::FVoxelGraphErrorReporter(const UVoxelGraphGenerator* VoxelGraphGenerator) : VoxelGraphGenerator(VoxelGraphGenerator) , Parent(nullptr) , ErrorPrefix("") { ensure(VoxelGraphGenerator); } FVoxelGraphErrorReporter::FVoxelGraphErrorReporter(FVoxelGraphErrorReporter& Parent, const FString& ErrorPrefix) : VoxelGraphGenerator(Parent.VoxelGraphGenerator) , Parent(&Parent) , ErrorPrefix(Parent.ErrorPrefix + ErrorPrefix + ": ") { ensure(VoxelGraphGenerator); } FVoxelGraphErrorReporter::~FVoxelGraphErrorReporter() { if (Parent) { Parent->CopyFrom(*this); } } void FVoxelGraphErrorReporter::AddError(const FString& Error) { if (!Error.IsEmpty()) { const FString ErrorWithPrefix = AddPrefixToError(Error); Messages.Add(FVoxelGraphMessage{ nullptr, Error, EVoxelGraphNodeMessageType::Error }); bHasError = true; } } void FVoxelGraphErrorReporter::AddInternalError(const FString Error) { ensureMsgf(false, TEXT("Internal voxel graph compiler error: %s"), *Error); const bool bOldHasErrors = bHasError; AddError("Internal error: " + Error + "\nPlease create a bug report here: https://gitlab.com/Phyronnaz/VoxelPluginIssues/issues \n" "Don't forget to attach the generated header file"); bHasError = bOldHasErrors; } inline FString& GetErrorString(UVoxelGraphNodeInterface* Node, EVoxelGraphNodeMessageType Type) { switch (Type) { default: ensure(false); case EVoxelGraphNodeMessageType::Info: return Node->InfoMsg; case EVoxelGraphNodeMessageType::Warning: return Node->WarningMsg; case EVoxelGraphNodeMessageType::Error: return Node->ErrorMsg; } } void FVoxelGraphErrorReporter::AddMessageToNode( const UVoxelNode* Node, const FString& Message, EVoxelGraphNodeMessageType Severity, bool bSelectNode, bool bShowInList) { check(Node); const FString MessageWithPrefix = AddPrefixToError(Message); if (Severity == EVoxelGraphNodeMessageType::Error) { bHasError = true; } if (bShowInList) { FVoxelGraphMessage NewMessage; NewMessage.Node = Node; NewMessage.Message = MessageWithPrefix; NewMessage.Type = Severity; Messages.Add(NewMessage); } if (bSelectNode) { AddNodeToSelect(Node); } #if WITH_EDITORONLY_DATA if (UVoxelGraphNodeInterface* GraphNode = Node->GraphNode) { AddMessageToNodeInternal(Node, MessageWithPrefix, Severity); GraphsToRefresh.Add(GraphNode->GetGraph()); } #endif } void FVoxelGraphErrorReporter::AddNodeToSelect(const UVoxelNode* Node) { #if WITH_EDITORONLY_DATA if (Node && Node->GraphNode) { NodesToSelect.Add(Node->GraphNode); } #endif } void FVoxelGraphErrorReporter::Apply(bool bSelectNodes) { #if WITH_EDITOR if (VoxelGraphGenerator && VoxelGraphGenerator->VoxelGraph) { GraphsToRefresh.Add(VoxelGraphGenerator->VoxelGraph); if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) { for (auto* GraphToRefresh : GraphsToRefresh) { VoxelGraphEditor->RefreshNodesMessages(GraphToRefresh); } if (NodesToSelect.Num() > 0 && bSelectNodes) { VoxelGraphEditor->SelectNodesAndZoomToFit(VoxelGraphGenerator->VoxelGraph, NodesToSelect.Array()); } VoxelGraphEditor->AddMessages(VoxelGraphGenerator, Messages); } } else #endif { for (auto& Message : Messages) { if (Message.Type == EVoxelGraphNodeMessageType::Error) { LOG_VOXEL(Warning, TEXT("%s failed to compile: %s"), VoxelGraphGenerator ? *VoxelGraphGenerator->GetName() : TEXT(""), *Message.Message); } } } } void FVoxelGraphErrorReporter::CopyFrom(FVoxelGraphErrorReporter& Other) { check(VoxelGraphGenerator == Other.VoxelGraphGenerator); bHasError |= Other.bHasError; Messages.Append(Other.Messages); NodesToSelect.Append(Other.NodesToSelect); GraphsToRefresh.Append(Other.GraphsToRefresh); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void FVoxelGraphErrorReporter::ClearMessages(const UVoxelGraphGenerator* Graph, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) { #if WITH_EDITOR if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) { VoxelGraphEditor->ClearMessages(Graph, bClearAll, MessagesToClear); } #endif } void FVoxelGraphErrorReporter::ClearNodesMessages(const UVoxelGraphGenerator* Graph, bool bRecursive, bool bClearAll, EVoxelGraphNodeMessageType MessagesToClear) { #if WITH_EDITOR if (!Graph->VoxelGraph) { return; } static TSet Stack; if (Stack.Contains(Graph)) { return; } Stack.Add(Graph); TSet Macros; for (auto Node : Graph->VoxelGraph->Nodes) { if (auto* Interface = Cast(Node)) { for (auto Type : TEnumRange()) { if (bClearAll || MessagesToClear == Type) { GetErrorString(Interface, Type).Empty(); } } if (bRecursive) { if (auto* MacroNode = Cast(Interface->GetVoxelNode())) { UVoxelGraphMacro* Macro = MacroNode->Macro; if (!Macros.Contains(Macro) && Macro) { Macros.Add(Macro); ClearNodesMessages(Macro, bRecursive, bClearAll, MessagesToClear); } } } } } if (auto* VoxelGraphEditor = IVoxelGraphEditor::GetVoxelGraphEditor()) { VoxelGraphEditor->RefreshNodesMessages(Graph->VoxelGraph); } Stack.Remove(Graph); #endif } void FVoxelGraphErrorReporter::ClearCompilationMessages(const UVoxelGraphGenerator* Graph) { for (auto Type : TEnumRange()) { ClearMessages(Graph, false, Type); ClearNodesMessages(Graph, true, false, Type); } } void FVoxelGraphErrorReporter::AddMessageToNodeInternal( const UVoxelNode* Node, const FString& Message, EVoxelGraphNodeMessageType Severity) { #if WITH_EDITOR if (UVoxelGraphNodeInterface* GraphNode = Node->GraphNode) { FString& Text = GetErrorString(GraphNode, Severity); if (!Text.IsEmpty()) { Text += "\n"; } Text += Message; } #endif } FString FVoxelGraphErrorReporter::AddPrefixToError(const FString& Error) const { if (!VoxelGraphGenerator || VoxelGraphGenerator->bDetailedErrors) { return ErrorPrefix + Error; } else { return Error; } }