EM_Task/UnrealEd/Classes/Commandlets/GatherTextFromSourceCommandlet.h
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

401 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Framework/Commands/Commands.h"
#include "Commandlets/GatherTextCommandletBase.h"
#include "Internationalization/StringTableCore.h"
#include "GatherTextFromSourceCommandlet.generated.h"
class Error;
enum class EGatherTextSourceFileTypes : uint8
{
None = 0,
Cpp = 1 << 0,
Ini = 1 << 1,
};
ENUM_CLASS_FLAGS(EGatherTextSourceFileTypes);
/**
* UGatherTextFromSourceCommandlet: Localization commandlet that collects all text to be localized from the source code.
*/
UCLASS()
class UGatherTextFromSourceCommandlet: public UGatherTextCommandletBase
{
GENERATED_UCLASS_BODY()
private:
#define LOC_DEFINE_REGION
enum class EEditorOnlyDefineState : uint8
{
Undefined,
Defined,
};
struct FSourceLocation
{
FSourceLocation()
: File(), Line(INDEX_NONE)
{
}
FSourceLocation(FString InFile, const int32 InLine)
: File(MoveTemp(InFile)), Line(InLine)
{
}
FString ToString() const
{
return (Line == INDEX_NONE) ? File : FString::Printf(TEXT("%s:%d"), *File, Line);
}
FString File;
int32 Line;
};
struct FParsedStringTableEntry
{
FString SourceString;
FSourceLocation SourceLocation;
FName PlatformName;
bool bIsEditorOnly;
};
struct FParsedStringTableEntryMetaData
{
FString MetaData;
FSourceLocation SourceLocation;
bool bIsEditorOnly;
};
typedef TMap<FName, FParsedStringTableEntryMetaData> FParsedStringTableEntryMetaDataMap;
struct FParsedStringTable
{
FString TableNamespace;
FSourceLocation SourceLocation;
TMap<FString, FParsedStringTableEntry, FDefaultSetAllocator, FLocKeyMapFuncs<FParsedStringTableEntry>> TableEntries;
TMap<FString, FParsedStringTableEntryMetaDataMap, FDefaultSetAllocator, FLocKeyMapFuncs<FParsedStringTableEntryMetaDataMap>> MetaDataEntries;
};
struct FSourceFileParseContext
{
bool AddManifestText(const FString& Token, const FString& Namespace, const FString& SourceText, const FManifestContext& Context);
void PushMacroBlock(const FString& InBlockCtx);
void PopMacroBlock();
void FlushMacroStack();
EEditorOnlyDefineState EvaluateEditorOnlyDefineState() const;
void SetDefine(const FString& InDefineCtx);
void RemoveDefine(const FString& InDefineCtx);
void AddStringTable(const FName InTableId, const FString& InTableNamespace);
void AddStringTableFromFile(const FName InTableId, const FString& InTableNamespace, const FString& InTableFilename, const FString& InRootPath);
void AddStringTableEntry(const FName InTableId, const FString& InKey, const FString& InSourceString);
void AddStringTableEntryMetaData(const FName InTableId, const FString& InKey, const FName InMetaDataId, const FString& InMetaData);
// Working data
EGatherTextSourceFileTypes FileTypes;
FString Filename;
int32 LineNumber;
FName FilePlatformName;
FString LineText;
FString Namespace;
FString RawStringLiteralClosingDelim;
bool ExcludedRegion;
bool EndParsingCurrentLine;
bool WithinBlockComment;
bool WithinLineComment;
bool WithinStringLiteral;
int32 WithinNamespaceDefineLineNumber;
const TCHAR* WithinStartingLine;
// Should editor-only data be included in this gather?
bool ShouldGatherFromEditorOnlyData;
// Discovered string table data from all files
TMap<FName, FParsedStringTable> ParsedStringTables;
FSourceFileParseContext(UGatherTextFromSourceCommandlet* InOwnerCommandlet)
: FileTypes(EGatherTextSourceFileTypes::None), Filename(), LineNumber(0), FilePlatformName(), LineText(), Namespace(), ExcludedRegion(false), EndParsingCurrentLine(false), WithinBlockComment(false), WithinLineComment(false), WithinStringLiteral(false), WithinNamespaceDefineLineNumber(INDEX_NONE), WithinStartingLine(nullptr), ShouldGatherFromEditorOnlyData(false), MacroBlockStack(), CachedEditorOnlyDefineState(), OwnerCommandlet(InOwnerCommandlet)
{
check(OwnerCommandlet);
}
private:
bool AddStringTableImpl(const FName InTableId, const FString& InTableNamespace);
bool AddStringTableEntryImpl(const FName InTableId, const FString& InKey, const FString& InSourceString, const FSourceLocation& InSourceLocation, const FName InPlatformName);
bool AddStringTableEntryMetaDataImpl(const FName InTableId, const FString& InKey, const FName InMetaDataId, const FString& InMetaData, const FSourceLocation& InSourceLocation);
// Working data
TArray<FString> MacroBlockStack;
mutable TOptional<EEditorOnlyDefineState> CachedEditorOnlyDefineState;
UGatherTextFromSourceCommandlet* OwnerCommandlet;
};
class FParsableDescriptor
{
public:
virtual ~FParsableDescriptor() = default;
virtual const FString& GetToken() const = 0;
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const = 0;
bool MatchesFileTypes(const EGatherTextSourceFileTypes InFileTypes) { return EnumHasAnyFlags(ApplicableFileTypes, InFileTypes); }
bool OverridesLongerTokens() { return bOverridesLongerTokens; }
protected:
EGatherTextSourceFileTypes ApplicableFileTypes = EGatherTextSourceFileTypes::None;
bool bOverridesLongerTokens = false;
};
class FPreProcessorDescriptor: public FParsableDescriptor
{
public:
FPreProcessorDescriptor()
{
ApplicableFileTypes = EGatherTextSourceFileTypes::Cpp;
bOverridesLongerTokens = true;
}
protected:
static const FString DefineString;
static const FString UndefString;
static const FString IfString;
static const FString IfDefString;
static const FString ElIfString;
static const FString ElseString;
static const FString EndIfString;
static const FString DefinedString;
static const FString IniNamespaceString;
};
class FDefineDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::DefineString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FUndefDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::UndefString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FIfDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::IfString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FIfDefDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::IfDefString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FElIfDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::ElIfString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FElseDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::ElseString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FEndIfDescriptor: public FPreProcessorDescriptor
{
public:
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::EndIfString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FMacroDescriptor: public FParsableDescriptor
{
public:
static const FString TextMacroString;
FMacroDescriptor(FString InName)
: Name(MoveTemp(InName))
{
ApplicableFileTypes = EGatherTextSourceFileTypes::Cpp;
}
virtual const FString& GetToken() const override { return Name; }
protected:
bool ParseArgsFromMacro(const FString& Text, TArray<FString>& Args, FSourceFileParseContext& Context) const;
static bool PrepareArgument(FString& Argument, bool IsAutoText, const FString& IdentForLogging, bool& OutHasQuotes);
private:
FString Name;
};
class FUICommandMacroDescriptor: public FMacroDescriptor
{
public:
FUICommandMacroDescriptor()
: FMacroDescriptor(TEXT("UI_COMMAND"))
{
}
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
protected:
FUICommandMacroDescriptor(FString InName)
: FMacroDescriptor(MoveTemp(InName))
{
}
void TryParseArgs(const FString& Text, FSourceFileParseContext& Context, const TArray<FString>& Arguments, const int32 ArgIndexOffset) const;
};
class FUICommandExtMacroDescriptor: public FUICommandMacroDescriptor
{
public:
FUICommandExtMacroDescriptor()
: FUICommandMacroDescriptor(TEXT("UI_COMMAND_EXT"))
{
}
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FStringMacroDescriptor: public FMacroDescriptor
{
public:
enum EMacroArgSemantic
{
MAS_Namespace,
MAS_Identifier,
MAS_SourceText,
};
struct FMacroArg
{
EMacroArgSemantic Semantic;
bool IsAutoText;
FMacroArg(EMacroArgSemantic InSema, bool InIsAutoText): Semantic(InSema), IsAutoText(InIsAutoText) {}
};
FStringMacroDescriptor(FString InName, FMacroArg Arg0, FMacroArg Arg1, FMacroArg Arg2): FMacroDescriptor(InName)
{
ApplicableFileTypes = EGatherTextSourceFileTypes::Cpp | EGatherTextSourceFileTypes::Ini;
Arguments.Add(Arg0);
Arguments.Add(Arg1);
Arguments.Add(Arg2);
}
FStringMacroDescriptor(FString InName, FMacroArg Arg0, FMacroArg Arg1): FMacroDescriptor(InName)
{
ApplicableFileTypes = EGatherTextSourceFileTypes::Cpp | EGatherTextSourceFileTypes::Ini;
Arguments.Add(Arg0);
Arguments.Add(Arg1);
}
FStringMacroDescriptor(FString InName, FMacroArg Arg0): FMacroDescriptor(InName)
{
ApplicableFileTypes = EGatherTextSourceFileTypes::Cpp | EGatherTextSourceFileTypes::Ini;
Arguments.Add(Arg0);
}
virtual void TryParse(const FString& LineText, FSourceFileParseContext& Context) const override;
private:
TArray<FMacroArg> Arguments;
};
class FStringTableMacroDescriptor: public FMacroDescriptor
{
public:
FStringTableMacroDescriptor()
: FMacroDescriptor(TEXT("LOCTABLE_NEW"))
{
}
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FStringTableFromFileMacroDescriptor: public FMacroDescriptor
{
public:
FStringTableFromFileMacroDescriptor(FString InName, FString InRootPath): FMacroDescriptor(MoveTemp(InName)), RootPath(MoveTemp(InRootPath)) {}
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
private:
FString RootPath;
};
class FStringTableEntryMacroDescriptor: public FMacroDescriptor
{
public:
FStringTableEntryMacroDescriptor()
: FMacroDescriptor(TEXT("LOCTABLE_SETSTRING"))
{
}
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FStringTableEntryMetaDataMacroDescriptor: public FMacroDescriptor
{
public:
FStringTableEntryMetaDataMacroDescriptor()
: FMacroDescriptor(TEXT("LOCTABLE_SETMETA"))
{
}
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
class FIniNamespaceDescriptor: public FPreProcessorDescriptor
{
public:
FIniNamespaceDescriptor()
{
ApplicableFileTypes = EGatherTextSourceFileTypes::Ini;
}
virtual const FString& GetToken() const override { return FPreProcessorDescriptor::IniNamespaceString; }
virtual void TryParse(const FString& Text, FSourceFileParseContext& Context) const override;
};
static const FString ChangelistName;
static FString UnescapeLiteralCharacterEscapeSequences(const FString& InString);
static FString RemoveStringFromTextMacro(const FString& TextMacro, const FString& IdentForLogging, bool& Error);
static FString StripCommentsFromToken(const FString& InToken, FSourceFileParseContext& Context);
static bool ParseSourceText(const FString& Text, const TArray<FParsableDescriptor*>& Parsables, FSourceFileParseContext& ParseCtxt);
public:
//~ Begin UCommandlet Interface
virtual int32 Main(const FString& Params) override;
//~ End UCommandlet Interface
#undef LOC_DEFINE_REGION
};