diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..35a9c91 --- /dev/null +++ b/.clang-format @@ -0,0 +1,145 @@ +--- +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -4 +AlignAfterOpenBracket: BlockIndent +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Left +AlignOperands: DontAlign +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: true +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: AfterColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakStringLiterals: true +ColumnLimit: 0 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: true +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: OuterScope +MaxEmptyLinesToKeep: 3 +NamespaceIndentation: None +PackConstructorInitializers: CurrentLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +QualifierAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google +ReferenceAlignment: Left +ReflowComments: true +SeparateDefinitionBlocks: Always +SortIncludes: Never +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Auto +StatementMacros: + - UPROEPRTY + - UFUNCTION + - UCLASS + - USTRUCT + - UENUM + - UMETA +TabWidth: 8 +UseCRLF: false +UseTab: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ebde5c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,83 @@ +# Visual Studio 2015 user specific files +.vs/ + +# Rider C++ +.idea/ +Plugins/Developer/RiderLink/ + +# Visual Studio 2015 database file +*.VC.db + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +*.ipa + +# These project files can be generated by the engine +*.xcodeproj +*.xcworkspace +*.sln +*.suo +*.opensdf +*.sdf +*.VC.db +*.VC.opendb + +# Precompiled Assets +SourceArt/**/*.png +SourceArt/**/*.tga + +# Binary Files +Binaries/* +Plugins/*/Binaries/* + +# Builds +Build/* + +# Whitelist PakBlacklist-.txt files +!Build/*/ +Build/*/** +!Build/*/PakBlacklist*.txt + +# Don't ignore icon files in Build +!Build/**/*.ico + +# Built data for maps +*_BuiltData.uasset + +# Configuration files generated by the Editor +Saved/* + +# Compiled source files for the engine to use +Intermediate/* +Plugins/*/Intermediate/* + +# Cache files for the editor to use +DerivedDataCache/* +*.DotSettings* +*DotSettings* diff --git a/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset b/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset new file mode 100644 index 0000000..83fa002 Binary files /dev/null and b/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset differ diff --git a/Plugins/Linter/Content/GamemakinLinter/GamemakinNamingConvention.uasset b/Content/GamemakinLinter/GamemakinNamingConvention.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/GamemakinNamingConvention.uasset rename to Content/GamemakinLinter/GamemakinNamingConvention.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Compiles.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Compiles.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Compiles.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Compiles.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MaxNodes.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MaxNodes.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MaxNodes.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MaxNodes.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MustReturn.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MustReturn.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MustReturn.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MustReturn.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_PublicDescriptions.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_PublicDescriptions.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_PublicDescriptions.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_PublicDescriptions.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_NoConfigFlag.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_NoConfigFlag.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_NoConfigFlag.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_NoConfigFlag.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_ConfigCategories.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_ConfigCategories.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_ConfigCategories.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Vars_ConfigCategories.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_EditableTooltips.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_EditableTooltips.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_EditableTooltips.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Vars_EditableTooltips.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoIs.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoIs.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoIs.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoIs.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoPluralArrays.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoPluralArrays.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoPluralArrays.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoPluralArrays.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NonAtomic.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NonAtomic.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NonAtomic.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NonAtomic.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_PascalCase.uasset b/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_PascalCase.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_PascalCase.uasset rename to Content/GamemakinLinter/LintRules/GMLR_BP_Vars_PascalCase.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_IsNamedCorrectly.uasset b/Content/GamemakinLinter/LintRules/GMLR_IsNamedCorrectly.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_IsNamedCorrectly.uasset rename to Content/GamemakinLinter/LintRules/GMLR_IsNamedCorrectly.uasset diff --git a/Content/GamemakinLinter/LintRules/GMLR_Level_LightingNeedsToBeRebuilt.uasset b/Content/GamemakinLinter/LintRules/GMLR_Level_LightingNeedsToBeRebuilt.uasset new file mode 100644 index 0000000..486bf3e Binary files /dev/null and b/Content/GamemakinLinter/LintRules/GMLR_Level_LightingNeedsToBeRebuilt.uasset differ diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Particles_EmitterNames.uasset b/Content/GamemakinLinter/LintRules/GMLR_Particles_EmitterNames.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Particles_EmitterNames.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Particles_EmitterNames.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_PathChecks.uasset b/Content/GamemakinLinter/LintRules/GMLR_PathChecks.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_PathChecks.uasset rename to Content/GamemakinLinter/LintRules/GMLR_PathChecks.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_DoNotNameAssets.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_DoNotNameAssets.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_DoNotNameAssets.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_DoNotNameAssets.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_Maps.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_Maps.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_Maps.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_Maps.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoSpaces.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_NoSpaces.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoSpaces.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_NoSpaces.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoTopLevelAssets.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_NoTopLevelAssets.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoTopLevelAssets.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_NoTopLevelAssets.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoUnicode.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_NoUnicode.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoUnicode.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_NoUnicode.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_PascalCase.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_PascalCase.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_PascalCase.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_PascalCase.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_RedundantNames.uasset b/Content/GamemakinLinter/LintRules/GMLR_Path_RedundantNames.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_RedundantNames.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Path_RedundantNames.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_StaticMesh_ValidUVs.uasset b/Content/GamemakinLinter/LintRules/GMLR_StaticMesh_ValidUVs.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_StaticMesh_ValidUVs.uasset rename to Content/GamemakinLinter/LintRules/GMLR_StaticMesh_ValidUVs.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_PowerOfTwo.uasset b/Content/GamemakinLinter/LintRules/GMLR_Texture2D_PowerOfTwo.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_PowerOfTwo.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Texture2D_PowerOfTwo.uasset diff --git a/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_Size_NotTooBig.uasset b/Content/GamemakinLinter/LintRules/GMLR_Texture2D_Size_NotTooBig.uasset similarity index 100% rename from Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_Size_NotTooBig.uasset rename to Content/GamemakinLinter/LintRules/GMLR_Texture2D_Size_NotTooBig.uasset diff --git a/Plugins/Linter/Content/Icons/CompileStatus_Fail_64px.png b/Content/Icons/CompileStatus_Fail_64px.png similarity index 100% rename from Plugins/Linter/Content/Icons/CompileStatus_Fail_64px.png rename to Content/Icons/CompileStatus_Fail_64px.png diff --git a/Plugins/Linter/Content/Icons/CompileStatus_Good_64px.png b/Content/Icons/CompileStatus_Good_64px.png similarity index 100% rename from Plugins/Linter/Content/Icons/CompileStatus_Good_64px.png rename to Content/Icons/CompileStatus_Good_64px.png diff --git a/Plugins/Linter/Content/Icons/CompileStatus_Unknown_64px.png b/Content/Icons/CompileStatus_Unknown_64px.png similarity index 100% rename from Plugins/Linter/Content/Icons/CompileStatus_Unknown_64px.png rename to Content/Icons/CompileStatus_Unknown_64px.png diff --git a/Plugins/Linter/Content/Icons/CompileStatus_Warning_64px.png b/Content/Icons/CompileStatus_Warning_64px.png similarity index 100% rename from Plugins/Linter/Content/Icons/CompileStatus_Warning_64px.png rename to Content/Icons/CompileStatus_Warning_64px.png diff --git a/Plugins/Linter/Content/Icons/CompileStatus_Working_64px.png b/Content/Icons/CompileStatus_Working_64px.png similarity index 100% rename from Plugins/Linter/Content/Icons/CompileStatus_Working_64px.png rename to Content/Icons/CompileStatus_Working_64px.png diff --git a/Plugins/Linter/Content/Icons/icon_AddContent_64x.png b/Content/Icons/icon_AddContent_64x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_AddContent_64x.png rename to Content/Icons/icon_AddContent_64x.png diff --git a/Plugins/Linter/Content/Icons/icon_Help_Documentation_16x.png b/Content/Icons/icon_Help_Documentation_16x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_Help_Documentation_16x.png rename to Content/Icons/icon_Help_Documentation_16x.png diff --git a/Plugins/Linter/Content/Icons/icon_Help_epic_16x.png b/Content/Icons/icon_Help_epic_16x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_Help_epic_16x.png rename to Content/Icons/icon_Help_epic_16x.png diff --git a/Plugins/Linter/Content/Icons/icon_MapCheck_64x.png b/Content/Icons/icon_MapCheck_64x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_MapCheck_64x.png rename to Content/Icons/icon_MapCheck_64x.png diff --git a/Plugins/Linter/Content/Icons/icon_MessageLog_Error_14x.png b/Content/Icons/icon_MessageLog_Error_14x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_MessageLog_Error_14x.png rename to Content/Icons/icon_MessageLog_Error_14x.png diff --git a/Plugins/Linter/Content/Icons/icon_MessageLog_Info_14x.png b/Content/Icons/icon_MessageLog_Info_14x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_MessageLog_Info_14x.png rename to Content/Icons/icon_MessageLog_Info_14x.png diff --git a/Plugins/Linter/Content/Icons/icon_MessageLog_Warning_14x.png b/Content/Icons/icon_MessageLog_Warning_14x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_MessageLog_Warning_14x.png rename to Content/Icons/icon_MessageLog_Warning_14x.png diff --git a/Plugins/Linter/Content/Icons/icon_Texture_Run_16x.png b/Content/Icons/icon_Texture_Run_16x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_Texture_Run_16x.png rename to Content/Icons/icon_Texture_Run_16x.png diff --git a/Plugins/Linter/Content/Icons/icon_file_saveall_64x.png b/Content/Icons/icon_file_saveall_64x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_file_saveall_64x.png rename to Content/Icons/icon_file_saveall_64x.png diff --git a/Plugins/Linter/Content/Icons/icon_tab_Toolbars_64x.png b/Content/Icons/icon_tab_Toolbars_64x.png similarity index 100% rename from Plugins/Linter/Content/Icons/icon_tab_Toolbars_64x.png rename to Content/Icons/icon_tab_Toolbars_64x.png diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.uasset b/Content/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_PathRules.uasset b/Content/MarketplaceLinter/LintRules/MPLR_PathRules.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_PathRules.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_PathRules.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_PascalCase.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Path_PascalCase.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_PascalCase.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Path_PascalCase.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.uasset b/Content/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_StaticMesh_ValidUVs.uasset b/Content/MarketplaceLinter/LintRules/MPLR_StaticMesh_ValidUVs.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_StaticMesh_ValidUVs.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_StaticMesh_ValidUVs.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.uasset b/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.uasset rename to Content/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/MarketplaceLintRuleSet.uasset b/Content/MarketplaceLinter/MarketplaceLintRuleSet.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/MarketplaceLintRuleSet.uasset rename to Content/MarketplaceLinter/MarketplaceLintRuleSet.uasset diff --git a/Plugins/Linter/Content/MarketplaceLinter/MarketplaceNamingConvention.uasset b/Content/MarketplaceLinter/MarketplaceNamingConvention.uasset similarity index 100% rename from Plugins/Linter/Content/MarketplaceLinter/MarketplaceNamingConvention.uasset rename to Content/MarketplaceLinter/MarketplaceNamingConvention.uasset diff --git a/LICENSE b/LICENSE index 0e41278..2184c01 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ MIT License Copyright (c) 2021 Unreal Engine Plug-ins +Copyright (c) 2023, Forschungszentrum Jülich GmbH. + Jonathan Windgassen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Plugins/Linter/Linter.uplugin b/Linter.uplugin similarity index 77% rename from Plugins/Linter/Linter.uplugin rename to Linter.uplugin index b8697f9..33e7f57 100644 --- a/Plugins/Linter/Linter.uplugin +++ b/Linter.uplugin @@ -1,18 +1,17 @@ { "FileVersion": 3, - "Version": 10, - "VersionName": "2.3", + "Version": 11, + "VersionName": "3.0", "FriendlyName": "Linter", "Description": "", "Category": "Other", - "CreatedBy": "Michael Allar", + "CreatedBy": "Michael Allar, Jonathan Windgassen", "CreatedByURL": "https://gamemak.in", "DocsURL": "http://discord.gamemak.in", - "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/ca0639af6339476da86fa3bcf15de8ec", "SupportURL": "http://discord.gamemak.in", - "EngineVersion": "4.26.0", "CanContainContent": true, - "Installed": true, + "Installed": false, + "EnabledByDefault": true, "Modules": [ { "Name": "Linter", @@ -20,7 +19,6 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", "Mac", "Linux" ], @@ -35,7 +33,6 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", "Mac", "Linux" ], @@ -50,7 +47,6 @@ "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", "Mac", "Linux" ], diff --git a/Plugins/Linter/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset b/Plugins/Linter/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset deleted file mode 100644 index 55509cf..0000000 Binary files a/Plugins/Linter/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset and /dev/null differ diff --git a/Plugins/Linter/Resources/LintReportTemplate.html b/Plugins/Linter/Resources/LintReportTemplate.html deleted file mode 100644 index e7a51f8..0000000 --- a/Plugins/Linter/Resources/LintReportTemplate.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - {% TITLE %} Lint Report - - - - - - - - - - - - - - - - -
-
-
-

Results - {% RESULTS %}

-
-
-
-
-
- - - - \ No newline at end of file diff --git a/Plugins/Linter/Source/GamemakinLinter/GamemakinLinter.Build.cs b/Plugins/Linter/Source/GamemakinLinter/GamemakinLinter.Build.cs deleted file mode 100644 index 4fdbab7..0000000 --- a/Plugins/Linter/Source/GamemakinLinter/GamemakinLinter.Build.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -using UnrealBuildTool; - -public class GamemakinLinter : ModuleRules -{ - public GamemakinLinter(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange( - new string[] - { - "Core", - "CoreUObject", - "Engine", - "Linter" - } - ); - - - PrivateDependencyModuleNames.AddRange( - new string[] - { - - } - ); - } -} diff --git a/Plugins/Linter/Source/GamemakinLinter/Private/GamemakinLinter.cpp b/Plugins/Linter/Source/GamemakinLinter/Private/GamemakinLinter.cpp deleted file mode 100644 index 23584cb..0000000 --- a/Plugins/Linter/Source/GamemakinLinter/Private/GamemakinLinter.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#include "GamemakinLinter.h" - -#define LOCTEXT_NAMESPACE "FGamemakinLinterModule" - -void FGamemakinLinterModule::StartupModule() -{ -// Super::StartupModule(); -} - -void FGamemakinLinterModule::ShutdownModule() -{ -// Super::ShutdownModule(); -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FGamemakinLinterModule, GamemakinLinter) -DEFINE_LOG_CATEGORY(LogGamemakinLinter); \ No newline at end of file diff --git a/Plugins/Linter/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp b/Plugins/Linter/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp deleted file mode 100644 index 965bf37..0000000 --- a/Plugins/Linter/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "GamemakinNamingConvention.h" - - -UGamemakinNamingConvention::UGamemakinNamingConvention(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -#define SCRIPT_PATH(ScriptPath) TSoftClassPtr(FSoftObjectPath(TEXT("/Script/" #ScriptPath))) -#define ADD_PREFIX(ClassName, Prefix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix)); -#define ADD_PREFIX_SUFFIX(ClassName, Prefix, Suffix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix, Suffix)); - - // Animation - ADD_PREFIX(AimOffsetBlendSpace, "AO_"); - ADD_PREFIX(AimOffsetBlendSpace1D, "AO_"); - ADD_PREFIX(AnimBlueprint, "ABP_"); - ADD_PREFIX(AnimComposite, "AC_"); - ADD_PREFIX(AnimMontage, "AM_"); - ADD_PREFIX(AnimSequence, "A_"); - ADD_PREFIX(BlendSpace, "BS_"); - ADD_PREFIX(BlendSpace1D, "BS_"); - ADD_PREFIX(MorphTarget, "MT_"); - ADD_PREFIX(Rig, "Rig_"); - ADD_PREFIX(SkeletalMesh, "SK_"); - ADD_PREFIX(Skeleton, "SKEL_"); - - - // Artificial Intelligence - ADD_PREFIX(AIController, "AIC_"); - ADD_PREFIX(BehaviorTree, "BT_"); - ADD_PREFIX(BlackboardData, "BB_"); - ADD_PREFIX(BTDecorator, "BTDecorator_"); - ADD_PREFIX(BTService, "BTService_"); - ADD_PREFIX(BTTaskNode, "BTTask_"); - - // Blueprints - ADD_PREFIX(Blueprint, "BP_"); - ADD_PREFIX(BlueprintFunctionLibrary, "BPFL_"); - ADD_PREFIX(Interface, "BPI_"); - ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("IntroTutorials.EditorTutorial"), "TBP_")); - ADD_PREFIX(UserDefinedEnum, "E"); - ADD_PREFIX(UserDefinedStruct, "F"); - - // Materials - ADD_PREFIX(Material, "M_"); - ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.Material"))), "PP_", TEXT(""), "PostProcess")); - ADD_PREFIX(MaterialFunction, "MF_"); - ADD_PREFIX(MaterialInstance, "MI_"); - ADD_PREFIX(MaterialInstanceConstant, "MI_"); - ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.MaterialInstance"))), "PPI_", TEXT(""), "PostProcess")); - ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.MaterialInstanceConstant"))), "PPI_", TEXT(""), "PostProcess")); - ADD_PREFIX(MaterialParameterCollection, "MPC_"); - ADD_PREFIX(SubsurfaceProfile, "SP_"); - - // Textures - ADD_PREFIX(Texture2D, "T_"); - ADD_PREFIX(TextureCube, "TC_"); - ADD_PREFIX(TextureRenderTarget2D, "RT_"); - ADD_PREFIX(TextureRenderTargetCube, "RTC_"); - ADD_PREFIX(TextureLightProfile, "TLP_"); - - // Media - ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaTexture"), "MT_")); - ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaPlayer"), "MP_")); - - // Miscellaneous - ADD_PREFIX(VectorFieldAnimated, "VFA_"); - ADD_PREFIX(CameraAnim, "CA_"); - ADD_PREFIX(CurveLinearColor, "Curve_"); - ADD_PREFIX(CurveTable, "Curve_"); - ADD_PREFIX(DataTable, "DT_"); - ADD_PREFIX(CurveFloat, "Curve_"); - ADD_PREFIX(ForceFeedbackEffect, "FFE_"); - ADD_PREFIX(MatineeAnimInterface, "Matinee_"); - ADD_PREFIX(ObjectLibrary, "OL_"); - ADD_PREFIX(VectorFieldStatic, "VF_"); - ADD_PREFIX(TouchInterface, "TI_"); - ADD_PREFIX(CurveVector, "Curve_"); - ADD_PREFIX(StaticMesh, "S_"); - - // Paper 2D - - // Physics - ADD_PREFIX(PhysicalMaterial, "PM_"); - ADD_PREFIX(PhysicsAsset, "PHYS_"); - - // Sounds - ADD_PREFIX(DialogueVoice, "DV_"); - ADD_PREFIX(DialogueWave, "DW_"); - ADD_PREFIX(ReverbEffect, "Reverb_"); - ADD_PREFIX(SoundAttenuation, "ATT_"); - ADD_PREFIX(SoundClass, ""); - ADD_PREFIX(SoundConcurrency, "_SC"); - ADD_PREFIX_SUFFIX(SoundCue, "A_", "_Cue"); - ADD_PREFIX(SoundMix, "Mix_"); - ADD_PREFIX(SoundWave, "A_"); - - // User Interface - ADD_PREFIX(Font, "Font_"); - ADD_PREFIX(SlateBrushAsset, "Brush_"); - ADD_PREFIX(SlateWidgetStyleAsset, "Style_"); - ADD_PREFIX(WidgetBlueprint, "WBP_"); - - // Effects - ADD_PREFIX(ParticleSystem, "PS_"); - -#undef ADD_PREFIX - - SortConventions(); -} - diff --git a/Plugins/Linter/Source/GamemakinLinter/Public/GamemakinLinter.h b/Plugins/Linter/Source/GamemakinLinter/Public/GamemakinLinter.h deleted file mode 100644 index ef402ae..0000000 --- a/Plugins/Linter/Source/GamemakinLinter/Public/GamemakinLinter.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "Modules/ModuleManager.h" - -DECLARE_LOG_CATEGORY_EXTERN(LogGamemakinLinter, Verbose, All); - -class GAMEMAKINLINTER_API FGamemakinLinterModule : public IModuleInterface -{ -public: - - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; - - virtual bool SupportsDynamicReloading() override - { - return false; - } - -private: - -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Linter.Build.cs b/Plugins/Linter/Source/Linter/Linter.Build.cs deleted file mode 100644 index 95412a7..0000000 --- a/Plugins/Linter/Source/Linter/Linter.Build.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -using UnrealBuildTool; - -public class Linter : ModuleRules -{ - public Linter(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange( - new string[] - { - "Core", - } - ); - - - PrivateDependencyModuleNames.AddRange( - new string[] - { - "CoreUObject", - "Engine", - "Slate", - "SlateCore", - "AppFramework", - "InputCore", - "UnrealEd", - "GraphEditor", - "AssetTools", - "EditorStyle", - "BlueprintGraph", - "PropertyEditor", - "LauncherPlatform", - "Projects", - "DesktopPlatform", - "Json", - "UATHelper" - // ... add private dependencies that you statically link with here ... - } - ); - - PublicIncludePathModuleNames.Add("Launch"); - -#if UE_4_20_OR_LATER - PublicDefinitions.Add("UE_4_20_OR_LATER=1"); -#endif - - } -} diff --git a/Plugins/Linter/Source/Linter/Private/BatchRenameTool/BatchRenameTool.cpp b/Plugins/Linter/Source/Linter/Private/BatchRenameTool/BatchRenameTool.cpp deleted file mode 100644 index cc43644..0000000 --- a/Plugins/Linter/Source/Linter/Private/BatchRenameTool/BatchRenameTool.cpp +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2016 Gamemakin LLC. All Rights Reserved. - -#include "BatchRenameTool/BatchRenameTool.h" -#include "Framework/Application/SlateApplication.h" -#include "Framework/Text/TextLayout.h" -#include "Widgets/Layout/SBox.h" -#include "Widgets/Text/STextBlock.h" -#include "Widgets/Input/SButton.h" -#include "Types/SlateEnums.h" -#include "Editor.h" -#include "AssetToolsModule.h" -#include "Modules/ModuleManager.h" -#include "IAssetTools.h" -#include "Widgets/Notifications/SNotificationList.h" -#include "Framework/Notifications/NotificationManager.h" -#include "Logging/MessageLog.h" -#include "Logging/TokenizedMessage.h" - -#define LOCTEXT_NAMESPACE "LinterBatchRenamer" - -FDlgBatchRenameTool::FDlgBatchRenameTool(const TArray Assets) - : bRemovePrefix(false) - , bRemoveSuffix(false) - , SelectedAssets(Assets) -{ - if (FSlateApplication::IsInitialized()) - { - DialogWindow = SNew(SWindow) - .Title(LOCTEXT("BatchRenameToolDlgTitle", "Batch Rename Tool")) - .SupportsMinimize(false).SupportsMaximize(false) - .SaneWindowPlacement(true) - .AutoCenter(EAutoCenter::PreferredWorkArea) - .ClientSize(FVector2D(350, 165)); - - TSharedPtr DialogWrapper = - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(4.0f) - [ - SAssignNew(DialogWidget, SDlgBatchRenameTool) - .ParentWindow(DialogWindow) - ]; - - DialogWindow->SetContent(DialogWrapper.ToSharedRef()); - } -} -FDlgBatchRenameTool::EResult FDlgBatchRenameTool::ShowModal() -{ - //Show Dialog - GEditor->EditorAddModalWindow(DialogWindow.ToSharedRef()); - EResult UserResponse = (EResult)DialogWidget->GetUserResponse(); - - if (UserResponse == EResult::Confirm) - { - Prefix = DialogWidget->PrefixTextBox->GetText().ToString(); - Suffix = DialogWidget->SuffixTextBox->GetText().ToString(); - bRemovePrefix = DialogWidget->PrefixRemoveBox->IsChecked(); - bRemoveSuffix = DialogWidget->SuffixRemoveBox->IsChecked(); - - Find = DialogWidget->FindTextBox->GetText().ToString(); - Replace = DialogWidget->ReplaceTextBox->GetText().ToString(); - - // If no information is given, treat as canceled - if (Prefix.IsEmpty() && Suffix.IsEmpty() && Find.IsEmpty()) - { - return EResult::Cancel; - } - - FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); - TArray AssetsAndNames; - - for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt) - { - const FAssetData& Asset = *AssetIt; - - // Early out on assets that can not be renamed - if (!(!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))) - { - continue; - } - - // Work on a copy of the asset name and see if after name operations - // if the copy is different than the original before creating rename data - FString AssetNewName = Asset.AssetName.ToString(); - - if (!Find.IsEmpty()) - { - AssetNewName.ReplaceInline(*Find, *Replace); - } - - if (!Prefix.IsEmpty()) - { - if (bRemovePrefix) - { - AssetNewName.RemoveFromStart(Prefix, ESearchCase::CaseSensitive); - } - else - { - if (!AssetNewName.StartsWith(Prefix, ESearchCase::CaseSensitive)) - { - AssetNewName.InsertAt(0, Prefix); - } - } - } - - if (!Suffix.IsEmpty()) - { - if (bRemoveSuffix) - { - AssetNewName.RemoveFromEnd(Suffix, ESearchCase::CaseSensitive); - } - else - { - if (!AssetNewName.EndsWith(Suffix, ESearchCase::CaseSensitive)) - { - AssetNewName = AssetNewName.Append(Suffix); - } - } - } - - if (AssetNewName != Asset.AssetName.ToString()) - { - AssetsAndNames.Push(FAssetRenameData(Asset.GetAsset(), Asset.PackagePath.ToString(), AssetNewName)); - } - } - - if (!AssetToolsModule.Get().RenameAssets(AssetsAndNames)) - { - FNotificationInfo NotificationInfo(LOCTEXT("BatchRenameFailed", "Batch Rename operation did not fully complete successfully. Maybe fix up redirectors? Check Output Log for details!")); - NotificationInfo.ExpireDuration = 6.0f; - NotificationInfo.Hyperlink = FSimpleDelegate::CreateStatic([]() { FMessageLog("LoadErrors").Open(EMessageSeverity::Info, true); }); - NotificationInfo.HyperlinkText = LOCTEXT("LoadObjectHyperlink", "Show Message Log"); - FSlateNotificationManager::Get().AddNotification(NotificationInfo); - } - } - return UserResponse; -} - -void SDlgBatchRenameTool::Construct(const FArguments& InArgs) -{ - UserResponse = FDlgBatchRenameTool::Cancel; - ParentWindow = InArgs._ParentWindow.Get(); - - this->ChildSlot[ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .HAlign(HAlign_Right) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SNew(SBox) - .WidthOverride(48.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BatchRenameToolDlgPrefix", "Prefix")) - .Justification(ETextJustify::Right) - ] - ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SAssignNew(PrefixTextBox, SEditableTextBox) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(0.0f, 0.0f, 0.0f, 0.0f) - [ - SAssignNew(PrefixRemoveBox, SCheckBox) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BatchRenameToolDlgRemove", "Remove")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .HAlign(HAlign_Right) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SNew(SBox) - .WidthOverride(48.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BatchRenameToolDlgSuffix", "Suffix")) - .Justification(ETextJustify::Right) - ] - ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SAssignNew(SuffixTextBox, SEditableTextBox) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(0.0f, 0.0f, 0.0f, 0.0f) - [ - SAssignNew(SuffixRemoveBox, SCheckBox) - ] - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BatchRenameToolDlgRemove", "Remove")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SSeparator) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SNew(SBox) - .WidthOverride(48.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BatchRenameToolDlgFind", "Find")) - .Justification(ETextJustify::Right) - ] - ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SAssignNew(FindTextBox, SEditableTextBox) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SNew(SBox) - .WidthOverride(48.0f) - [ - SNew(STextBlock) - .Text(LOCTEXT("BatchRenameToolDlgReplace", "Replace")) - .Justification(ETextJustify::Right) - ] - ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) - .Padding(0.0f, 0.0f, 8.0f, 0.0f) - [ - SAssignNew(ReplaceTextBox, SEditableTextBox) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SSeparator) - ] - + SVerticalBox::Slot() - .AutoHeight() - .HAlign(HAlign_Right) - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SUniformGridPanel) - .SlotPadding(FEditorStyle::GetMargin("StandardDialog.SlotPadding")) - .MinDesiredSlotWidth(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) - .MinDesiredSlotHeight(FEditorStyle::GetFloat("StandardDialog.MinDesiredSlotHeight")) - + SUniformGridPanel::Slot(0, 0) - [ - SNew(SButton) - .HAlign(HAlign_Center) - .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) - .OnClicked(this, &SDlgBatchRenameTool::OnButtonClick, FDlgBatchRenameTool::Confirm) - .Text(LOCTEXT("SkeletonMergeOk", "OK")) - ] - + SUniformGridPanel::Slot(1, 0) - [ - SNew(SButton) - .HAlign(HAlign_Center) - .ContentPadding(FEditorStyle::GetMargin("StandardDialog.ContentPadding")) - .OnClicked(this, &SDlgBatchRenameTool::OnButtonClick, FDlgBatchRenameTool::Cancel) - .Text(LOCTEXT("SkeletonMergeCancel", "Cancel")) - ] - ] - ]; -} - -FDlgBatchRenameTool::EResult SDlgBatchRenameTool::GetUserResponse() const -{ - return UserResponse; -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/Linter/Source/Linter/Private/LintRule.cpp b/Plugins/Linter/Source/Linter/Private/LintRule.cpp deleted file mode 100644 index 596aa31..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRule.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include "LintRule.h" - -#include "Runtime/Launch/Resources/Version.h" -#include "Materials/MaterialInterface.h" -#include "Materials/Material.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "Engine/Blueprint.h" -#include "AssetData.h" -#include "Modules/ModuleManager.h" -#include "IAssetRegistry.h" -#include "IAssetTools.h" -#include "AssetRegistryModule.h" - - -ULintRule::ULintRule(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - -} - -bool ULintRule::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, OUT TArray& OutRuleViolations) const -{ - return true; -} - -bool ULintRule::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - OutRuleViolations.Empty(); - - if (ObjectToLint == nullptr) - { - return true; - } - - if (ParentRuleSet == nullptr) - { - return true; - } - - if (IsRuleSuppressed()) - { - return true; - } - - return PassesRule_Internal(ObjectToLint, ParentRuleSet, OutRuleViolations); -} - -bool ULintRule::IsRuleSuppressed() const -{ - return false; -} - -FName ULintRule::GetRuleBasedObjectVariantName_Implementation(UObject* ObjectToLint) const -{ - if (ObjectToLint == nullptr) - { - return NAME_None; - } - - { - UMaterialInterface* MI = Cast(ObjectToLint); - if (MI != nullptr) - { -#if ENGINE_MINOR_VERSION >= 25 - TMicRecursionGuard RecursionGuard; -#else - UMaterialInterface::TMicRecursionGuard RecursionGuard; -#endif - const UMaterial* Material = MI->GetMaterial_Concurrent(RecursionGuard); - if (Material != nullptr) - { - if (Material->MaterialDomain == EMaterialDomain::MD_PostProcess) - { - return "PostProcess"; - } - } - } - } - - { - UBlueprint* Blueprint = Cast(ObjectToLint); - if (Blueprint != nullptr) - { - if (Blueprint->BlueprintType == EBlueprintType::BPTYPE_MacroLibrary) - { - return "MacroLibrary"; - } - - if (FBlueprintEditorUtils::IsInterfaceBlueprint(Blueprint)) - { - return "Interface"; - } - - if (Blueprint->BlueprintType == BPTYPE_FunctionLibrary) - { - return "FunctionLibrary"; - } - } - } - - return NAME_None; -} - -TArray FLintRuleViolation::AllRuleViolationsWithViolator(const TArray& RuleViolationCollection, const UObject* SearchViolator) -{ - return RuleViolationCollection.FilterByPredicate([SearchViolator](const FLintRuleViolation& RuleViolation) - { - if (SearchViolator != nullptr && RuleViolation.Violator.Get() == SearchViolator) - { - return true; - } - - return false; - }); -} - -TArray> FLintRuleViolation::AllRuleViolationsWithViolatorShared(const TArray>& RuleViolationCollection, const UObject* SearchViolator) -{ - return RuleViolationCollection.FilterByPredicate([SearchViolator](const TSharedPtr& RuleViolation) - { - if (SearchViolator != nullptr && RuleViolation->Violator.Get() == SearchViolator) - { - return true; - } - - return false; - }); -} - -TArray> FLintRuleViolation::AllRuleViolationsWithViolatorShared(const TArray& RuleViolationCollection, const UObject* SearchViolator) -{ - // This should really be done when the structs are first created - TArray> SharedViolations; - TArray Violations = AllRuleViolationsWithViolator(RuleViolationCollection, SearchViolator); - for (FLintRuleViolation Violation : Violations) - { - SharedViolations.Push(TSharedPtr(new FLintRuleViolation(Violation))); - } - - return SharedViolations; -} - -TArray FLintRuleViolation::AllRuleViolationsOfSpecificRule(const TArray& RuleViolationCollection, TSubclassOf SearchRule) -{ - return RuleViolationCollection.FilterByPredicate([SearchRule](const FLintRuleViolation& RuleViolation) - { - if (SearchRule.Get() != nullptr && RuleViolation.ViolatedRule == SearchRule) - { - return true; - } - - return false; - }); -} - -TArray FLintRuleViolation::AllRuleViolationsOfRuleGroup(const TArray& RuleViolationCollection, FName SearchRuleGroup) -{ - return RuleViolationCollection.FilterByPredicate([SearchRuleGroup](const FLintRuleViolation& RuleViolation) - { - if (RuleViolation.ViolatedRule.Get() != nullptr && RuleViolation.ViolatedRule.GetDefaultObject()->RuleGroup == SearchRuleGroup) - { - return true; - } - - return false; - }); -} - -TArray FLintRuleViolation::AllRuleViolationViolators(const TArray& RuleViolationCollection) -{ - TArray Violators; - for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) - { - Violators.Add(RuleViolation.Violator.Get()); - } - return Violators; -} - -TArray FLintRuleViolation::AllRuleViolationViolators(const TArray>& RuleViolationCollection) -{ - TArray Violators; - for (const TSharedPtr& RuleViolation : RuleViolationCollection) - { - Violators.AddUnique(RuleViolation->Violator.Get()); - } - return Violators; -} - -TMultiMap FLintRuleViolation::AllRuleViolationsMappedByViolator(const TArray& RuleViolationCollection) -{ - TMultiMap ViolatorViolationsMultiMap; - - for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) - { - ViolatorViolationsMultiMap.Add(RuleViolation.Violator.Get(), RuleViolation); - } - - return ViolatorViolationsMultiMap; -} - -TMultiMap FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRule(const TArray& RuleViolationCollection) -{ - TMultiMap LintRuleViolationsMultiMap; - - for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) - { - LintRuleViolationsMultiMap.Add(RuleViolation.ViolatedRule.GetDefaultObject(), RuleViolation); - } - - return LintRuleViolationsMultiMap; -} - -TMultiMap> FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRuleShared(const TArray& RuleViolationCollection) -{ - // We should really just create shared ptrs to begin with instead of doing this - TMultiMap> LintRuleViolationsMultiMap; - - for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) - { - LintRuleViolationsMultiMap.Add(RuleViolation.ViolatedRule.GetDefaultObject(), TSharedPtr(new FLintRuleViolation(RuleViolation))); - } - - return LintRuleViolationsMultiMap; -} - -TMultiMap> FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRuleShared(const TArray>& RuleViolationCollection) -{ - TMultiMap> LintRuleViolationsMultiMap; - - for (const TSharedPtr& RuleViolation : RuleViolationCollection) - { - LintRuleViolationsMultiMap.Add(RuleViolation->ViolatedRule.GetDefaultObject(), RuleViolation); - } - - return LintRuleViolationsMultiMap; -} - -bool FLintRuleViolation::PopulateAssetData() -{ - IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked("AssetRegistry").Get(); - TArray AssetRenameData; - - if (Violator.IsValid()) - { - ViolatorAssetData = AssetRegistry.GetAssetByObjectPath(FName(*Violator->GetPathName())); - return ViolatorAssetData.IsValid(); - } - - return false; -} diff --git a/Plugins/Linter/Source/Linter/Private/LintRuleSet.cpp b/Plugins/Linter/Source/Linter/Private/LintRuleSet.cpp deleted file mode 100644 index e52ae60..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRuleSet.cpp +++ /dev/null @@ -1,182 +0,0 @@ -#include "LintRuleSet.h" -#include "LintRunner.h" - -#include "AssetRegistryModule.h" -#include "Modules/ModuleManager.h" -#include "HAL/RunnableThread.h" - -ULintRuleSet::ULintRuleSet(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - -} - -ULinterNamingConvention* ULintRuleSet::GetNamingConvention() const -{ - return NamingConvention.Get(); -} - -TArray ULintRuleSet::LintPath(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask /*= nullptr*/) const -{ - NamingConvention.LoadSynchronous(); - - TArray RuleViolations; - - if (AssetPaths.Num() == 0) - { - AssetPaths.Push(TEXT("/Game")); - } - - // Begin loading assets - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); - - UE_LOG(LogLinter, Display, TEXT("Loading the asset registry...")); - AssetRegistryModule.Get().SearchAllAssets(/*bSynchronousSearch =*/true); - UE_LOG(LogLinter, Display, TEXT("Finished loading the asset registry. Loading assets...")); - - TArray AssetList; - - FARFilter ARFilter; - ARFilter.bRecursivePaths = true; - - for (const FString& AssetPath : AssetPaths) - { - UE_LOG(LogLinter, Display, TEXT("Adding path \"%s\" to be linted."), *AssetPath); - ARFilter.PackagePaths.Push(FName(*AssetPath)); - } - - AssetRegistryModule.Get().GetAssets(ARFilter, AssetList); - - TArray LintRunners; - TArray Threads; - - if (ParentScopedSlowTask != nullptr) - { - ParentScopedSlowTask->TotalAmountOfWork = AssetList.Num() + 2; - ParentScopedSlowTask->CompletedWork = 0.0f; - } - - for (FAssetData const& Asset : AssetList) - { - check(Asset.IsValid()); - UE_LOG(LogLinter, Verbose, TEXT("Creating Lint Thread for asset \"%s\"."), *Asset.AssetName.ToString()); - UObject* Object = Asset.GetAsset(); - check(Object != nullptr); - - FLintRunner* Runner = new FLintRunner(Object, this, &RuleViolations, ParentScopedSlowTask); - check(Runner != nullptr); - - LintRunners.Add(Runner); - - if (Runner->RequiresGamethread()) - { - Runner->Run(); - // If we're given a scoped slow task, update its progress now... - if (ParentScopedSlowTask != nullptr) - { - ParentScopedSlowTask->EnterProgressFrame(1.0f); - } - } - else - { - Threads.Push(FRunnableThread::Create(Runner, *FString::Printf(TEXT("FLintRunner - %s"), *Asset.ObjectPath.ToString()), 0, TPri_Normal)); - if (ParentScopedSlowTask != nullptr) - { - ParentScopedSlowTask->EnterProgressFrame(1.0f); - } - } - } - - for (FRunnableThread* Thread : Threads) - { - Thread->WaitForCompletion(); - } - - if (ParentScopedSlowTask != nullptr) - { - ParentScopedSlowTask->EnterProgressFrame(1.0f, NSLOCTEXT("Linter", "ScanTaskFinished", "Tabulating Data...")); - } - - return RuleViolations; -} - -TArray> ULintRuleSet::LintPathShared(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask /*= nullptr*/) const -{ - TArray RuleViolations = LintPath(AssetPaths, ParentScopedSlowTask); - - TArray> SharedRuleViolations; - for (FLintRuleViolation Violation : RuleViolations) - { - TSharedPtr SharedViolation = TSharedPtr(new FLintRuleViolation(Violation)); - SharedViolation->PopulateAssetData(); - SharedRuleViolations.Push(SharedViolation); - } - - return SharedRuleViolations; -} - -const FLintRuleList* ULintRuleSet::GetLintRuleListForClass(TSoftClassPtr Class) const -{ - UClass* searchClass = Class.LoadSynchronous(); - while (searchClass != nullptr) - { - const FLintRuleList* pRuleList = ClassLintRulesMap.Find(searchClass); - if (pRuleList != nullptr) - { - return pRuleList; - } - - // @HACK: If we reach UObject, find our hack rule for fallback - if (searchClass == UObject::StaticClass()) - { - const FLintRuleList* anyObjectRuleList = ClassLintRulesMap.Find(UAnyObject_LinterDummyClass::StaticClass()); - return anyObjectRuleList; - } - - // Load our parent class in case we failed to get naming conventions - searchClass = searchClass->GetSuperClass(); - } - - return nullptr; -} - -bool FLintRuleList::RequiresGameThread() const -{ - for (TSubclassOf LintRuleSubClass : LintRules) - { - UClass* LintClass = LintRuleSubClass.Get(); - if (LintClass != nullptr) - { - const ULintRule* LintRule = GetDefault(LintClass); - if (LintRule != nullptr && LintRule->bRequiresGameThread) - { - return true; - } - } - } - - return false; -} - -bool FLintRuleList::PassesRules(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - OutRuleViolations.Empty(); - - bool bFailedAnyRule = false; - for (TSubclassOf LintRuleSubClass : LintRules) - { - UClass* LintClass = LintRuleSubClass.Get(); - if (LintClass != nullptr) - { - const ULintRule* LintRule = GetDefault(LintClass); - if (LintRule != nullptr) - { - TArray ViolatedRules; - bFailedAnyRule = !LintRule->PassesRule(ObjectToLint, ParentRuleSet, ViolatedRules) || bFailedAnyRule; - OutRuleViolations.Append(ViolatedRules); - } - } - } - - return !bFailedAnyRule; -} diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp deleted file mode 100644 index ca3f32a..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Compiles.h" -#include "LintRuleSet.h" -#include "Sound/SoundWave.h" -#include "Engine/Blueprint.h" - -ULintRule_Blueprint_Compiles::ULintRule_Blueprint_Compiles(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Compiles::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - switch (Blueprint->Status) - { - case EBlueprintStatus::BS_BeingCreated: - case EBlueprintStatus::BS_Dirty: - case EBlueprintStatus::BS_Unknown: - case EBlueprintStatus::BS_UpToDate: - return true; - case EBlueprintStatus::BS_Error: - case EBlueprintStatus::BS_UpToDateWithWarnings: - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); - return false; - default: - return true; - } -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp deleted file mode 100644 index 1e522ef..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Funcs_MaxNodes.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" -#include "K2Node_FunctionResult.h" -#include "K2Node_Knot.h" -#include "K2Node_FunctionEntry.h" -#include "K2Node_Self.h" -#include "K2Node_DynamicCast.h" -#include "K2Node_BreakStruct.h" -#include "EdGraphNode_Comment.h" -#include "K2Node_VariableGet.h" -#include "K2Node_StructMemberGet.h" -#include "K2Node_Tunnel.h" -#include "K2Node_TemporaryVariable.h" -#include "K2Node_FunctionTerminator.h" -#include "K2Node_CastByteToEnum.h" -#include "K2Node_CallFunction.h" - -ULintRule_Blueprint_Funcs_MaxNodes::ULintRule_Blueprint_Funcs_MaxNodes(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Funcs_MaxNodes::IsNodeTrivial(const UEdGraphNode* Node) -{ - if (Node->IsA(UK2Node_Knot::StaticClass()) - || Node->IsA(UK2Node_FunctionEntry::StaticClass()) - || Node->IsA(UK2Node_Self::StaticClass()) - || Node->IsA(UK2Node_DynamicCast::StaticClass()) - || Node->IsA(UK2Node_BreakStruct::StaticClass()) - || Node->IsA(UEdGraphNode_Comment::StaticClass()) - || Node->IsA(UK2Node_VariableGet::StaticClass()) - || Node->IsA(UK2Node_StructMemberGet::StaticClass()) - || Node->IsA(UK2Node_Tunnel::StaticClass()) - || Node->IsA(UK2Node_TemporaryVariable::StaticClass()) - || Node->IsA(UK2Node_FunctionResult::StaticClass()) - || Node->IsA(UK2Node_FunctionTerminator::StaticClass()) - || Node->IsA(UK2Node_CastByteToEnum::StaticClass()) - ) - { - return true; - } - - if (const UK2Node_CallFunction* CallFuncNode = Cast(Node)) - { - FName FuncName = CallFuncNode->FunctionReference.GetMemberName(); - if (FuncName == TEXT("Conv_InterfaceToObject") - || FuncName.ToString().Contains(TEXT("MakeLiteral")) - || FuncName.ToString().Contains(TEXT("ToString")) - || FuncName.ToString().StartsWith(TEXT("Make")) - || FuncName.ToString().StartsWith(TEXT("Break")) - ) - { - return true; - } - } - - return false; -} - -bool ULintRule_Blueprint_Funcs_MaxNodes::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsMaxNodes", "{Previous}{WhiteSpace}Please simply function {FuncName} as it has {Nodes} nodes when we want a max of {MaxNodes}."); - FText AllFixes; - - for (auto FunctionGraph : Blueprint->FunctionGraphs) - { - if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) - { - // If initial graph check exceeds node limit, filter out nodes that do not contribute to complexity - if (FunctionGraph->Nodes.Num() > MaxExpectedNonTrivialNodes) - { - auto NodesCopy = FunctionGraph->Nodes; - NodesCopy.RemoveAll([this](UEdGraphNode* Val) { return IsNodeTrivial(Val); }); - - // If removing knots and comments still exceeds node limit, report error - if (NodesCopy.Num() > MaxExpectedNonTrivialNodes) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("Nodes"), FText::FromString(FString::FromInt(NodesCopy.Num())), TEXT("MaxNodes"), FText::FromString(FString::FromInt(MaxExpectedNonTrivialNodes)), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp deleted file mode 100644 index b3afc19..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" -#include "K2Node_FunctionResult.h" - -ULintRule_Blueprint_Funcs_MustHaveReturn::ULintRule_Blueprint_Funcs_MustHaveReturn(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Funcs_MustHaveReturn::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - // Early return out if blueprint type shouldn't be checked for return nodes - switch (Blueprint->BlueprintType) - { - case BPTYPE_Normal: - case BPTYPE_Const: - case BPTYPE_LevelScript: - case BPTYPE_FunctionLibrary: - break; - case BPTYPE_MacroLibrary: - case BPTYPE_Interface: - default: - return true; - } - - bool bRuleViolated = false; - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsMustHaveReturn", "{Previous}{WhiteSpace}Please give function {FuncName} a return node."); - FText AllFixes; - - static const FName DefaultAnimGraphName("AnimGraph"); - - for (auto FunctionGraph : Blueprint->FunctionGraphs) - { - if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript - && FunctionGraph->GetFName() != DefaultAnimGraphName) - { - TArray AllResultNodes; - FunctionGraph->GetNodesOfClass(AllResultNodes); - if (AllResultNodes.Num() <= 0) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp deleted file mode 100644 index e192779..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" -#include "K2Node_FunctionEntry.h" - - -ULintRule_Blueprint_Funcs_PublicDescriptions::ULintRule_Blueprint_Funcs_PublicDescriptions(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Funcs_PublicDescriptions::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - // Early return out if blueprint type shouldn't be checked for function descriptions - switch (Blueprint->BlueprintType) - { - case BPTYPE_Normal: - case BPTYPE_Const: - case BPTYPE_LevelScript: - case BPTYPE_FunctionLibrary: - break; - case BPTYPE_MacroLibrary: - case BPTYPE_Interface: - default: - return true; - } - - bool bRuleViolated = false; - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsPublicDescriptions", "{Previous}{WhiteSpace}Please give public function {FuncName} a description."); - FText AllFixes; - - for (UEdGraph* FunctionGraph : Blueprint->FunctionGraphs) - { - if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) - { - UK2Node_FunctionEntry* FunctionEntryNode = nullptr; - TArray EntryNodes; - - FunctionGraph->GetNodesOfClass(EntryNodes); - - if ((EntryNodes.Num() > 0) && EntryNodes[0]->IsEditable()) - { - FunctionEntryNode = Cast(EntryNodes[0]); - } - - if (FunctionEntryNode != nullptr) - { - if (FUNC_AccessSpecifiers & FunctionEntryNode->GetFunctionFlags() & FUNC_Public) - { - if (FunctionEntryNode->MetaData.ToolTip.IsEmpty()) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - } - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp deleted file mode 100644 index 6bc3cb1..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_LooseNodes.h" -#include "LintRuleSet.h" -#include "Sound/SoundWave.h" -#include "Engine/Blueprint.h" -#include "EdGraph/EdGraphPin.h" -#include "EdGraph/EdGraphNode.h" -#include "EdGraph/EdGraph.h" -#include "EdGraphNode_Comment.h" -#include "K2Node_Event.h" -#include "K2Node_FunctionEntry.h" -#include "K2Node_Knot.h" -#include "K2Node_Tunnel.h" - -ULintRule_Blueprint_LooseNodes::ULintRule_Blueprint_LooseNodes(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_LooseNodes::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - // Check for loose nodes - TArray Graphs; - Blueprint->GetAllGraphs(Graphs); - - for (UEdGraph* Graph : Graphs) - { - for (UEdGraphNode* Node : Graph->Nodes) - { - if ( - Node->IsAutomaticallyPlacedGhostNode() || - Node->IsA(UK2Node_Event::StaticClass()) || - Node->IsA(UK2Node_FunctionEntry::StaticClass()) || - Node->IsA(UK2Node_Knot::StaticClass()) || - Node->IsA(UEdGraphNode_Comment::StaticClass()) || - Node->IsA(UK2Node_Tunnel::StaticClass()) - ) - { - continue; - } - - bool bNodeIsolated = true; - - TArray Pins = Node->GetAllPins(); - for (UEdGraphPin* Pin : Pins) - { - if (Pin->LinkedTo.Num() != 0) - { - bNodeIsolated = false; - break; - } - } - - if (bNodeIsolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); - return false; - } - } - } - - return true; - -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp deleted file mode 100644 index 58b797d..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Vars_ConfigCategories.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" - -ULintRule_Blueprint_Vars_ConfigCategories::ULintRule_Blueprint_Vars_ConfigCategories(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Vars_ConfigCategories::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsConfigCategories", "{Previous}{WhiteSpace}Please give variable {VarName} a category."); - FText FixTextTemplateEditable = NSLOCTEXT("Linter", "BlueprintVarsConfigCategoriesEditable", "{Previous}{WhiteSpace}Please give editable variable {VarName} a category starting with 'Config'."); - FText AllFixes; - - int32 VariableCount = Blueprint->NewVariables.Num(); - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - if (FBlueprintEditorUtils::IsVariableComponent(Desc)) - { - VariableCount--; - } - } - - if (VariableCount < NumVariablesToRequireCategorization) - { - return true; - } - - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - FString PropName = Desc.VarName.ToString(); - FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); - - // Is Editable variable? - if ((Desc.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) - { - if (!Desc.Category.ToString().StartsWith(TEXT("Config"))) - { - AllFixes = FText::FormatNamed(FixTextTemplateEditable, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - continue; - } - } - else - { - if (Desc.Category.IsEmptyOrWhitespace()) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp deleted file mode 100644 index c614ec7..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" - -ULintRule_Blueprint_Vars_EditableMustHaveTooltip::ULintRule_Blueprint_Vars_EditableMustHaveTooltip(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Vars_EditableMustHaveTooltip::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsEditableMustHaveTooltip", "{Previous}{WhiteSpace}Please give variable {VarName} a tooltip as it is marked editable."); - FText AllFixes; - - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - FString PropName = Desc.VarName.ToString(); - FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); - - if ((Desc.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) - { - if (!Desc.HasMetaData(FBlueprintMetadata::MD_Tooltip) || Desc.GetMetaData(FBlueprintMetadata::MD_Tooltip).Len() <= 0) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp deleted file mode 100644 index 53fc50b..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" - -ULintRule_Blueprint_Vars_NoConfigFlag::ULintRule_Blueprint_Vars_NoConfigFlag(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Vars_NoConfigFlag::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsNoConfigFlag", "{Previous}{WhiteSpace}Please disable the config flag on variable {VarName}."); - FText AllFixes; - - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - FString PropName = Desc.VarName.ToString(); - FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); - - if ((Desc.PropertyFlags & CPF_Config) == CPF_Config) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp deleted file mode 100644 index a2b1ef9..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Vars_NonAtomic.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" -#include "Internationalization/Regex.h" - -ULintRule_Blueprint_Vars_NonAtomic::ULintRule_Blueprint_Vars_NonAtomic(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - - -bool ULintRule_Blueprint_Vars_NonAtomic::IsVariableAtomic(FBPVariableDescription& VarDesc) -{ - return (VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean - || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Byte - || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Int - || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Float - || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_String - || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Enum - ); -} - -bool ULintRule_Blueprint_Vars_NonAtomic::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsNonAtomic", "{Previous}{WhiteSpace}Please fix variable named {VarName}."); - FText AllFixes; - - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - FString PropName = Desc.VarName.ToString(); - FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); - - if (IsVariableAtomic(Desc) && PropName.Contains(TypeName.ToString())) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp deleted file mode 100644 index 22ccc94..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Vars_PluralArrays.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" - -ULintRule_Blueprint_Vars_PluralArrays::ULintRule_Blueprint_Vars_PluralArrays(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Blueprint_Vars_PluralArrays::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "PluralArrayHasArray", "{Previous}{WhiteSpace}Please remove the word 'Array' from your variable {VarName}."); - FText AllFixes; - - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - FString PropName = Desc.VarName.ToString(); - FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); - - if (PropName.Contains(TEXT("Array"), ESearchCase::CaseSensitive)) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp deleted file mode 100644 index 01d0d91..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Blueprint_Vars_Regex.h" -#include "LintRuleSet.h" -#include "Engine/Blueprint.h" -#include "EdGraphSchema_K2.h" -#include "Internationalization/Regex.h" - -ULintRule_Blueprint_Vars_Regex::ULintRule_Blueprint_Vars_Regex(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - - -bool ULintRule_Blueprint_Vars_Regex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - UBlueprint* Blueprint = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsRegex", "{Previous}{WhiteSpace}Please fix variable named {VarName}."); - FText AllFixes; - - FString TestRegexPatternString = RegexPatternString; - FString BoolTestRegexPatternString = TEXT("b") + RegexPatternString; - - const FRegexPattern TestRegexPattern = FRegexPattern(TestRegexPatternString); - const FRegexPattern BoolTestRegexPattern = FRegexPattern(BoolTestRegexPatternString); - - for (FBPVariableDescription Desc : Blueprint->NewVariables) - { - FString PropName = Desc.VarName.ToString(); - FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); - bool bIsBool = Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean; - - FRegexMatcher Matcher(bIsBool ? BoolTestRegexPattern : TestRegexPattern, PropName); - bool bFoundMatch = Matcher.FindNext(); - - if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Collection.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Collection.cpp deleted file mode 100644 index 50d06a4..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Collection.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Collection.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_Collection::ULintRule_Collection(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Collection::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - bool bRuleViolated = false; - - for (TSubclassOf LintRuleClass : SubRules) - { - if (LintRuleClass.Get() != nullptr) - { - const ULintRule* LintRule = GetDefault(LintRuleClass); - if (LintRule != nullptr) - { - TArray SubViolations; - if (!LintRule->PassesRule(ObjectToLint, ParentRuleSet, SubViolations)) - { - OutRuleViolations.Append(SubViolations); - bRuleViolated = true; - } - } - } - } - - return !bRuleViolated; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp deleted file mode 100644 index 3b278f8..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_IsNamedCorrectly_Base.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" - -ULintRule_IsNamedCorrectly_Base::ULintRule_IsNamedCorrectly_Base(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - -} - -bool ULintRule_IsNamedCorrectly_Base::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // NameSettingList Contributed by RocknRolla#3102 on http://discord.gamemak.in - TArray NameSettingList; - // If ObjectToLint is a Blueprint, it's class hierarchy does not contain actual classes that Blueprints extend (like Actor, Character, PlayerController, etc.). - // So we try to use Blueprint's ParentClass to access it's class hierarchy. - if (ObjectToLint->IsA()) - { - const TSubclassOf BlueprintClass = Cast(ObjectToLint)->ParentClass; - if (BlueprintClass != nullptr) - { - NameSettingList = ParentRuleSet->GetNamingConvention()->GetNamingConventionsForClassVariant(TSoftClassPtr(BlueprintClass), GetRuleBasedObjectVariantName(ObjectToLint)); - } - } - // If ObjectToLint is not a Blueprint or we failed to find any conventions, just fall back to our default algorithm. - if (NameSettingList.Num() == 0) - { - NameSettingList = ParentRuleSet->GetNamingConvention()->GetNamingConventionsForClassVariant(ObjectToLint->GetClass(), GetRuleBasedObjectVariantName(ObjectToLint)); - } - - // If we don't have a name rule for this type of asset, simply return true - if (NameSettingList.Num() == 0) - { - return true; - } - - bool bFoundMatchingNameRule = false; - for (FLinterNamingConventionInfo Info : NameSettingList) - { - bool bPassesPrefixCheck = Info.Prefix.IsEmpty() ? true : ObjectToLint->GetName().StartsWith(Info.Prefix, ESearchCase::CaseSensitive); - bool bPassesSuffixCheck = Info.Suffix.IsEmpty() ? true : ObjectToLint->GetName().EndsWith(Info.Suffix, ESearchCase::CaseSensitive); - // Run prefix and suffix checks using found name settings if they are non-null - - if (bPassesPrefixCheck && bPassesSuffixCheck) - { - bFoundMatchingNameRule = true; - break; - } - } - - if (!bFoundMatchingNameRule) - { - FString SuggestedName = BuildSuggestedName(ObjectToLint->GetName(), NameSettingList[0].Prefix, NameSettingList[0].Suffix); - FText RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "IsNamedCorrectly_RecommendedAction", "Recommended name: [{0}]."), FText::FromString(SuggestedName)); - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); - return false; - } - - // If we don't have name settings or passed all name checks, simply return true - return true; -} - -FString ULintRule_IsNamedCorrectly_Base::BuildSuggestedName(FString CurrentName, FString DesiredPrefix, FString DesiredSuffix /*= TEXT("")*/) -{ - FString SuggestedName; - - int32 FirstUnderscore = CurrentName.Find(TEXT("_")); - int32 LastUnderscore = CurrentName.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd); - - bool bAddPrefix = false; - bool bAddSuffix = false; - - // Attempt to remove a bad prefix - if (!DesiredPrefix.IsEmpty() && !CurrentName.StartsWith(DesiredPrefix, ESearchCase::CaseSensitive)) - { - bAddPrefix = true; - if (FirstUnderscore <= 3) - { - CurrentName = CurrentName.RightChop(FirstUnderscore + 1); - } - } - - // Attempt to remove a bad suffix - if (!DesiredSuffix.IsEmpty() && !CurrentName.EndsWith(DesiredSuffix, ESearchCase::CaseSensitive)) - { - bAddSuffix = true; - if (CurrentName.Len() - LastUnderscore <= 3) - { - CurrentName = CurrentName.LeftChop(CurrentName.Len() - LastUnderscore); - } - } - - SuggestedName = CurrentName; - - if (bAddPrefix) - { - SuggestedName = DesiredPrefix + SuggestedName; - } - - if (bAddSuffix) - { - SuggestedName = SuggestedName + DesiredSuffix; - } - - return SuggestedName; -} diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp deleted file mode 100644 index a28b576..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_ParticleSystem_EmitterNameRegex.h" -#include "LintRuleSet.h" -#include "Particles/ParticleEmitter.h" -#include "Particles/ParticleSystem.h" -#include "Internationalization/Regex.h" - -ULintRule_ParticleSystem_EmitterNameRegex::ULintRule_ParticleSystem_EmitterNameRegex(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) - , RegexPatternString(TEXT("Particle Emitter")) -{ - DisallowedRecommendedAction = NSLOCTEXT("Linter", "ULintRule_ParticleSystem_EmitterRegexName_Disallowed", "Please rename the emitter \"{0}\" as you have multiple emitters."); - NonConformingRecommendedAction = NSLOCTEXT("Linter", "ULintRule_ParticleSystem_EmitterRegexName_NonConforming", "Please rename \"{0}\" as this emitter has invalid characters."); -} - -bool ULintRule_ParticleSystem_EmitterNameRegex::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // If we aren't a particle system, abort - if (Cast(ObjectToLint) == nullptr) - { - // @TODO: Bubble up some sort of configuration error? - return true; - } - - return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); -} - -bool ULintRule_ParticleSystem_EmitterNameRegex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - const UParticleSystem* ParticleSystem = CastChecked(ObjectToLint); - - bool bRuleViolated = false; - - FText FixTextTemplate = NSLOCTEXT("Linter", "ParticleHasBadEmitterNames", "{Previous}{WhiteSpace}Please rename emitter {EmitterName}."); - FText AllFixes; - - if (ParticleSystem->Emitters.Num() >= MinEmittersNeededToEnforce) - { - FRegexPattern RegexPattern = FRegexPattern(RegexPatternString); - - for (UParticleEmitter* Emitter : ParticleSystem->Emitters) - { - FRegexMatcher RegexMatcher(RegexPattern, Emitter->EmitterName.ToString()); - bool bFoundMatch = RegexMatcher.FindNext(); - - if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) - { - AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("EmitterName"), FText::FromString(Emitter->EmitterName.ToString()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); - bRuleViolated = true; - } - } - } - - if (bRuleViolated) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp deleted file mode 100644 index acd081a..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Path_DisallowNames.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_Path_DisallowNames::ULintRule_Path_DisallowNames(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - RecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_DisallowNames_ChangeName", "Please rename \"{0}\" to an allowed name."); -} - -bool ULintRule_Path_DisallowNames::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - FString PathName = ObjectToLint->GetPathName(); - TArray PathElements; - PathName.ParseIntoArray(PathElements, TEXT("/"), true); - - bool bRuleViolated = false; - - for (int32 i = 0; i < PathElements.Num() - 1; ++i) - { - if (DisallowedFolderNames.Contains(PathElements[i])) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, FText::FromString(PathElements[i])))); - bRuleViolated = true; - } - } - - return !bRuleViolated; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp deleted file mode 100644 index 7d16d02..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Path_IsNotTooLong.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_Path_IsNotTooLong::ULintRule_Path_IsNotTooLong(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Path_IsNotTooLong::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - FString PathName = ObjectToLint->GetPathName(); - - // See if file path is longer than 140 characters - // 145 = 140 + /Game (5) - int32 DotIndex = -1; - PathName.FindLastChar('.', DotIndex); - FString FilePath = PathName.LeftChop(PathName.Len() - DotIndex); - if (FilePath.Len() >= MaxPathLimit + 5) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp deleted file mode 100644 index 0c78641..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Path_NoTopLevel.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_Path_NoTopLevel::ULintRule_Path_NoTopLevel(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - ZeroTopLevelFoldersRecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_NoTopLevel_ZeroTopLevelFolders", "There appears to be no top level folders. Please put your assets in a top level folder."); - PleaseUseThisFolderRecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_NoTopLevel_PleaseUseThisFolder", "Please move this asset into a top level folder. Maybe \"{0}\"?"); -} - -bool ULintRule_Path_NoTopLevel::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - FString PathName = ObjectToLint->GetPathName(); - TArray PathElements; - PathName.ParseIntoArray(PathElements, TEXT("/"), true); - - // Report issue with top assets not in a top level folder - if (PathElements.Num() == 1) - { - FText RecommendedAction; - - // This is really slow to do for every single asset that fails to be in a top level folder - // But doing it here makes the code base a lot cleaner and easier to follow for now. - { - // Determine content sub directory structure for project organization based rules - TArray Subdirectories; - IFileManager::Get().FindFiles(Subdirectories, *(FPaths::ProjectContentDir() / TEXT("*")), false, true); - Subdirectories.Remove(TEXT("Collections")); - Subdirectories.Remove(TEXT("Developers")); - - if (Subdirectories.Num() == 0) - { - RecommendedAction = ZeroTopLevelFoldersRecommendedAction; - } - else - { - FString MostPopulatedContentDir; - int32 FileCount = 0; - for (FString Subdirectory : Subdirectories) - { - TArray FileNames; - IFileManager::Get().FindFilesRecursive(FileNames, *(FPaths::ProjectContentDir() / Subdirectory), TEXT("*"), true, false, false); - if (FileNames.Num() > FileCount) - { - FileCount = FileNames.Num(); - MostPopulatedContentDir = Subdirectory; - } - } - - RecommendedAction = FText::FormatOrdered(PleaseUseThisFolderRecommendedAction, FText::FromString(MostPopulatedContentDir)); - } - } - - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp deleted file mode 100644 index f4497ff..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Path_Regex.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" -#include "Internationalization/Regex.h" - -ULintRule_Path_Regex::ULintRule_Path_Regex(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) - , RegexPatternString(TEXT("[^a-zA-Z0-9_]")) -{ - DisallowedPathElementRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_DisallowedPathElement", "Please rename \"{0}\" and remove disallowed characters."); - NonConformingPathElementRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_NonConformingPathElement", "Please rename \"{0}\" and to conform to allowed characters."); - - DisallowedWholePathRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_DisallowedWholePath", "Please rename and remove disallowed characters."); - NonConformingWholePathRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_NonConformingWholePath", "Please rename and conform to allowed characters."); -} - -bool ULintRule_Path_Regex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - FString PathName = ObjectToLint->GetPathName(); - - FRegexPattern RegexPattern = FRegexPattern(RegexPatternString); - bool bRuleViolated = false; - - if (bCheckPerPathElement) - { - TArray PathElements; - PathName.ParseIntoArray(PathElements, TEXT("/"), true); - - for (int32 i = 0; i < PathElements.Num() - 1; ++i) - { - FRegexMatcher RegexMatcher(RegexPattern, PathElements[i]); - bool bFoundMatch = RegexMatcher.FindNext(); - - if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(bMustNotContainRegexPattern ? DisallowedPathElementRecommendedAction : NonConformingPathElementRecommendedAction, FText::FromString(PathElements[i])))); - bRuleViolated = true; - } - } - } - else - { - FRegexMatcher RegexMatcher(RegexPattern, PathName); - bool bFoundMatch = RegexMatcher.FindNext(); - - if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) - { - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(bMustNotContainRegexPattern ? DisallowedWholePathRecommendedAction : NonConformingWholePathRecommendedAction, FText::FromString(PathName)))); - bRuleViolated = true; - } - } - - return !bRuleViolated; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp deleted file mode 100644 index 63199b6..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_SoundWave_SampleRate.h" -#include "LintRuleSet.h" -#include "Sound/SoundWave.h" - -ULintRule_SoundWave_SampleRate::ULintRule_SoundWave_SampleRate(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - ValidSampleRates.Push(22050.0f); - ValidSampleRates.Push(44100.0f); -} - -bool ULintRule_SoundWave_SampleRate::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // If we aren't a sound wave, abort - if (Cast(ObjectToLint) == nullptr) - { - // @TODO: Bubble up some sort of configuration error? - return true; - } - - return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); -} - -bool ULintRule_SoundWave_SampleRate::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - USoundWave* SoundWave = const_cast(CastChecked(ObjectToLint)); - - if (ValidSampleRates.Contains(SoundWave->GetSampleRateForCurrentPlatform())) - { - return true; - } - - FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_SoundWave_SampleRate_Fix", "Please fix your sample rate of {0}."); - RecommendedAction = FText::FormatOrdered(RecommendedAction, SoundWave->GetSampleRateForCurrentPlatform()); - - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp deleted file mode 100644 index d4160a5..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_StaticMesh_ValidUVs.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_StaticMesh_ValidUVs::ULintRule_StaticMesh_ValidUVs(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - //UStaticMesh::CheckLightMapUVs requires being ran on the game thread - bRequiresGameThread = true; -} - -bool ULintRule_StaticMesh_ValidUVs::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // If we aren't a static mesh, abort - if (Cast(ObjectToLint) == nullptr) - { - // @TODO: Bubble up some sort of configuration error? - return true; - } - - return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); -} - -bool ULintRule_StaticMesh_ValidUVs::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - const UStaticMesh* StaticMesh = CastChecked(ObjectToLint); - - bool bHadErrors = false; - - TArray MissingUVs; - TArray BadUVs; - TArray ValidUVs; - - UStaticMesh::CheckLightMapUVs(const_cast(StaticMesh), MissingUVs, BadUVs, ValidUVs, true); - - if ((!bIgnoreMissingUVs && MissingUVs.Num() > 0)) - { - FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_StaticMesh_ValidUVs_Missing", "Static mesh has missing UVs. Please add at least one valid UV channel."); - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); - return false; - } - - if (BadUVs.Num() > 0) - { - FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_StaticMesh_ValidUVs_Bad", "Static mesh has invalid UVs. [{0}]"); - FText::FormatOrdered(RecommendedAction, FText::FromString(FString::Join(BadUVs, TEXT(", ")))); - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp deleted file mode 100644 index ab8088a..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Texture_Size_NotTooBig.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_Texture_Size_NotTooBig::ULintRule_Texture_Size_NotTooBig(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} - -bool ULintRule_Texture_Size_NotTooBig::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // If we aren't a texture, abort - if (Cast(ObjectToLint) == nullptr) - { - // @TODO: Bubble up some sort of configuration error? - return true; - } - - return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); -} - -bool ULintRule_Texture_Size_NotTooBig::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - const UTexture2D* Texture = CastChecked(ObjectToLint); - - int32 TexSizeX = Texture->GetSizeX(); - int32 TexSizeY = Texture->GetSizeY(); - - // Check to see if textures are too big - if (TexSizeX > MaxTextureSizeX || TexSizeY > MaxTextureSizeY) - { - FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_Texture_Size_NotTooBig_TooBig", "Please shrink your textures dimensions so that they fit within {0}x{1} pixels."); - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, MaxTextureSizeX, MaxTextureSizeY))); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp b/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp deleted file mode 100644 index 9819c71..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "LintRules/LintRule_Texture_Size_PowerOfTwo.h" -#include "LintRuleSet.h" -#include "LinterNamingConvention.h" -#include "HAL/FileManager.h" - -ULintRule_Texture_Size_PowerOfTwo::ULintRule_Texture_Size_PowerOfTwo(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - IgnoreTexturesInTheseGroups.Add(TextureGroup::TEXTUREGROUP_UI); -} - -bool ULintRule_Texture_Size_PowerOfTwo::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // If we aren't a texture, abort - if (Cast(ObjectToLint) == nullptr) - { - // @TODO: Bubble up some sort of configuration error? - return true; - } - - // If we're to ignore this texture LOD group, abort - if (IgnoreTexturesInTheseGroups.Contains(Cast(ObjectToLint)->LODGroup)) - { - return true; - } - - return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); -} - -bool ULintRule_Texture_Size_PowerOfTwo::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - const UTexture2D* Texture = CastChecked(ObjectToLint); - - int32 TexSizeX = Texture->GetSizeX(); - int32 TexSizeY = Texture->GetSizeY(); - - bool bXFail = ((TexSizeX & (TexSizeX - 1)) != 0); - bool bYFail = ((TexSizeY & (TexSizeY - 1)) != 0); - - UEnum* TextureGroupEnum = StaticEnum(); - FString IgnoredLODGroupNames; - - for (TEnumAsByte LODGroup : IgnoreTexturesInTheseGroups) - { - IgnoredLODGroupNames += TextureGroupEnum->GetMetaData(TEXT("DisplayName"), LODGroup) + TEXT(", "); - } - IgnoredLODGroupNames.RemoveFromEnd(TEXT(", ")); - - FText IgnoredLODGroupTip = IgnoredLODGroupNames.Len() > 0 ? FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_AllowedLODGroups", ". Alternatively, assign this texture to one of these LOD Groups: [{0}]"), FText::FromString(IgnoredLODGroupNames)) : FText::GetEmpty(); - - if (bXFail || bYFail) - { - FText RecommendedAction; - if (bXFail && bYFail) - { - RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_XY", "Please fix the width and height of this texture, currently {0} by {1}{2}"), TexSizeX, TexSizeY, IgnoredLODGroupTip); - } - else if (bXFail) - { - RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_X", "Please fix the width of this texture, currently {0}{1}"), TexSizeX, IgnoredLODGroupTip); - } - else if (bYFail) - { - RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_Y", "Please fix the height of this texture, currently {0}{1}"), TexSizeY, IgnoredLODGroupTip); - } - - OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); - return false; - } - - return true; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LintRunner.cpp b/Plugins/Linter/Source/Linter/Private/LintRunner.cpp deleted file mode 100644 index c151244..0000000 --- a/Plugins/Linter/Source/Linter/Private/LintRunner.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. -#include "LintRunner.h" - -#define LOCTEXT_NAMESPACE "Linter" - -FCriticalSection FLintRunner::LintDataUpdateLock; - -FLintRunner::FLintRunner(UObject* InLoadedObject, const ULintRuleSet* LintRuleSet, TArray* InpOutRuleViolations, FScopedSlowTask* InParentScopedSlowTask) - : LoadedObject(InLoadedObject) - , RuleSet(LintRuleSet) - , pOutRuleViolations(InpOutRuleViolations) - , pLoadedRuleList(LintRuleSet != nullptr ? LintRuleSet->GetLintRuleListForClass(InLoadedObject->GetClass()) : nullptr) - , ParentScopedSlowTask(InParentScopedSlowTask) -{ -} - -bool FLintRunner::RequiresGamethread() -{ - if (pLoadedRuleList != nullptr) - { - return pLoadedRuleList->RequiresGameThread(); - } - - return false; -} - -bool FLintRunner::Init() -{ - if (LoadedObject == nullptr) - { - return false; - } - - if (RuleSet == nullptr) - { - return false; - } - - if (pLoadedRuleList == nullptr) - { - return false; - } - - if (pOutRuleViolations == nullptr) - { - return false; - } - - return true; -} - -uint32 FLintRunner::Run() -{ - if (LoadedObject == nullptr || pLoadedRuleList == nullptr || RuleSet == nullptr || pOutRuleViolations == nullptr) - { - return 2; - } - - FString const AssetPath = LoadedObject->GetPathName(); - UE_LOG(LogLinter, Display, TEXT("Loaded '%s'..."), *AssetPath); - - TArray RuleViolations; - pLoadedRuleList->PassesRules(LoadedObject, RuleSet, RuleViolations); - - if (RuleViolations.Num() > 0) - { - FScopeLock lock(&LintDataUpdateLock); - pOutRuleViolations->Append(RuleViolations); - } - - UE_LOG(LogLinter, Display, TEXT("Finished '%s'..."), *AssetPath); - return 0; -} - -void FLintRunner::Stop() -{ - return; // this runner doesn't have anything on-going so there is nothing to stop -} - -void FLintRunner::Exit() -{ - return; -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/Linter/Source/Linter/Private/Linter.cpp b/Plugins/Linter/Source/Linter/Private/Linter.cpp deleted file mode 100644 index deab22e..0000000 --- a/Plugins/Linter/Source/Linter/Private/Linter.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. - -#include "Linter.h" -#include "ISettingsModule.h" -#include "Framework/Docking/TabManager.h" -#include "LevelEditor.h" -#include "Widgets/Input/SButton.h" -#include "Styling/SlateStyle.h" -#include "AssetRegistryModule.h" -#include "IAssetRegistry.h" -#include "AssetData.h" -#include "ContentBrowserModule.h" -#include "PropertyEditorModule.h" - -#include "LinterStyle.h" -#include "LinterContentBrowserExtensions.h" -#include "LinterNamingConvention.h" -#include "LinterSettings.h" -#include "UI/LintWizard.h" -#include "LintRuleSet.h" - -#define LOCTEXT_NAMESPACE "FLinterModule" - -static const FName LinterTabName = "LinterTab"; - -void FLinterModule::StartupModule() -{ - // Load the asset registry module - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); - IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); - - if (AssetRegistry.IsLoadingAssets()) - { - AssetRegistry.OnFilesLoaded().AddRaw(this, &FLinterModule::OnInitialAssetRegistrySearchComplete); - } - else - { - OnInitialAssetRegistrySearchComplete(); - } - - // Integrate Linter actions into existing editor context menus - if (!IsRunningCommandlet()) - { - // Register slate style overrides - FLinterStyle::Initialize(); - TSharedPtr StyleSetPtr = FLinterStyle::StyleSet; - - if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) - { - SettingsModule->RegisterSettings("Project", "Plugins", "Linter", - LOCTEXT("RuntimeSettingsName", "Linter"), - LOCTEXT("RuntimeSettingsDescription", "Configure the Linter plugin"), - GetMutableDefault()); - } - - // Install UI Hooks - FLinterContentBrowserExtensions::InstallHooks(this, &ContentBrowserExtenderDelegateHandle, &AssetExtenderDelegateHandle); - - //Register our UI - FGlobalTabmanager::Get()->RegisterNomadTabSpawner( - LinterTabName, - FOnSpawnTab::CreateStatic(&FLinterModule::SpawnTab, StyleSetPtr)) - .SetDisplayName(LOCTEXT("LinterTabName", "Linter")) - .SetTooltipText(LOCTEXT("LinterTabToolTip", "Linter")) - .SetMenuType(ETabSpawnerMenuType::Hidden); - - FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); - PropertyModule.RegisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FLinterNamingConventionDetails::MakeInstance)); - } -} - -void FLinterModule::ShutdownModule() -{ - if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) - { - SettingsModule->UnregisterSettings("Project", "Plugins", "Linter"); - } - - if (UObjectInitialized()) - { - FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); - PropertyModule.UnregisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName()); - - FLinterContentBrowserExtensions::RemoveHooks(this, &ContentBrowserExtenderDelegateHandle, &AssetExtenderDelegateHandle); - - if (FModuleManager::Get().IsModuleLoaded(TEXT("LevelEditor"))) - { - FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); - LevelEditorModule.OnTabManagerChanged().Remove(LevelEditorTabManagerChangedHandle); - } - - FGlobalTabmanager::Get()->UnregisterTabSpawner(LinterTabName); - - // Unregister slate style overrides - FLinterStyle::Shutdown(); - } -} - - -TSharedRef FLinterModule::SpawnTab(const FSpawnTabArgs& TabSpawnArgs, TSharedPtr StyleSet) -{ - const FSlateBrush* IconBrush = StyleSet->GetBrush("Linter.Toolbar.Icon"); - - const TSharedRef MajorTab = - SNew(SDockTab) - .Icon(IconBrush) - .TabRole(ETabRole::MajorTab); - - MajorTab->SetContent(SNew(SLintWizard)); - - return MajorTab; -} - -void FLinterModule::OnInitialAssetRegistrySearchComplete() -{ - TryToLoadAllLintRuleSets(); -} - -void FLinterModule::TryToLoadAllLintRuleSets() -{ - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); - IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); - - TArray FoundRuleSets; - AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); - - // Attempt to get all RuleSets in memory so that linting tools are better aware of them - for (const FAssetData& RuleSetData : FoundRuleSets) - { - if (!RuleSetData.IsAssetLoaded()) - { - RuleSetData.GetAsset(); - } - } -} - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FLinterModule, Linter) -DEFINE_LOG_CATEGORY(LogLinter); \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LinterCommandlet.cpp b/Plugins/Linter/Source/Linter/Private/LinterCommandlet.cpp deleted file mode 100644 index a455379..0000000 --- a/Plugins/Linter/Source/Linter/Private/LinterCommandlet.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2020 Gamemakin LLC. All Rights Reserved. - -#include "LinterCommandlet.h" -#include "Editor.h" -#include "AssetRegistryModule.h" -#include "AssetData.h" -#include "Engine/ObjectLibrary.h" -#include "Dom/JsonObject.h" -#include "Dom/JsonValue.h" -#include "Interfaces/IPluginManager.h" -#include "Misc/FileHelper.h" -#include "Misc/Paths.h" -#include "Serialization/JsonWriter.h" -#include "Serialization/JsonSerializer.h" -#include "Linter.h" -#include "LintRule.h" - -DEFINE_LOG_CATEGORY_STATIC(LinterCommandlet, All, All); - -ULinterCommandlet::ULinterCommandlet(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - IsClient = false; - IsServer = false; -} - -static void PrintUsage() -{ - UE_LOG(LinterCommandlet, Display, TEXT("Linter Usage: {Editor}.exe Project.uproject -run=Linter \"/Game/\"")); - UE_LOG(LinterCommandlet, Display, TEXT("")); - UE_LOG(LinterCommandlet, Display, TEXT("This will run the Linter on the provided project and will scan the supplied directory, example being the project's full Content/Game tree. Can add multiple paths as additional arguments.")); -} - -int32 ULinterCommandlet::Main(const FString& InParams) -{ - FString Params = InParams; - // Parse command line. - TArray Paths; - TArray Switches; - TMap ParamsMap; - UCommandlet::ParseCommandLine(*Params, Paths, Switches, ParamsMap); - - UE_LOG(LinterCommandlet, Display, TEXT("Linter is indeed running!")); - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); - - UE_LOG(LinterCommandlet, Display, TEXT("Loading the asset registry...")); - AssetRegistryModule.Get().SearchAllAssets(/*bSynchronousSearch =*/true); - UE_LOG(LinterCommandlet, Display, TEXT("Finished loading the asset registry. Determining Rule Set...")); - - ULintRuleSet* RuleSet = GetDefault()->DefaultLintRuleSet.LoadSynchronous(); - if (ParamsMap.Contains(TEXT("RuleSet"))) - { - const FString RuleSetName = *ParamsMap.FindChecked(TEXT("RuleSet")); - UE_LOG(LinterCommandlet, Display, TEXT("Trying to find Rule Set with Commandlet Name: %s"), *RuleSetName); - - FLinterModule::TryToLoadAllLintRuleSets(); - - TArray FoundRuleSets; - AssetRegistryModule.Get().GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); - - for (const FAssetData& RuleSetData : FoundRuleSets) - { - ULintRuleSet* LoadedRuleSet = Cast(RuleSetData.GetAsset()); - if (LoadedRuleSet != nullptr && LoadedRuleSet->NameForCommandlet == RuleSetName) - { - RuleSet = LoadedRuleSet; - UE_LOG(LinterCommandlet, Display, TEXT("Found Rule Set for name %s: %s"), *RuleSetName, *RuleSet->GetFullName()); - } - } - } - else - { - UE_LOG(LinterCommandlet, Display, TEXT("Using default rule set...")); - } - - if (RuleSet == nullptr) - { - UE_LOG(LinterCommandlet, Error, TEXT("Failed to load a rule set. Aborting. Returning error code 1.")); - return 1; - } - - UE_LOG(LinterCommandlet, Display, TEXT("Using rule set: %s"), *RuleSet->GetFullName()); - - if (Paths.Num() == 0) - { - Paths.Add(TEXT("/Game")); - } - - UE_LOG(LinterCommandlet, Display, TEXT("Attempting to Lint paths: %s"), *FString::Join(Paths, TEXT(", "))); - - const TArray RuleViolations = RuleSet->LintPath(Paths); - - int32 NumErrors = 0; - int32 NumWarnings = 0; - - for (const FLintRuleViolation& Violation : RuleViolations) - { - if (Violation.ViolatedRule->GetDefaultObject()->RuleSeverity <= ELintRuleSeverity::Error) - { - NumErrors++; - } - else - { - NumWarnings++; - } - } - - FString ResultsString = FText::FormatNamed(FText::FromString("Lint completed with {NumWarnings} {NumWarnings}|plural(one=warning,other=warnings), {NumErrors} {NumErrors}|plural(one=error,other=errors)."), TEXT("NumWarnings"), FText::FromString(FString::FromInt(NumWarnings)), TEXT("NumErrors"), FText::FromString(FString::FromInt(NumErrors))).ToString(); - UE_LOG(LinterCommandlet, Display, TEXT("Lint completed with %s."), *ResultsString); - - bool bWriteReport = Switches.Contains(TEXT("json")) || ParamsMap.Contains(TEXT("json")) || Switches.Contains(TEXT("html")) || ParamsMap.Contains(TEXT("html")); - if (bWriteReport) - { - UE_LOG(LinterCommandlet, Display, TEXT("Generating output report...")); - - TSharedPtr RootJsonObject = MakeShareable(new FJsonObject); - TArray> ViolatorJsonObjects; - - TArray UniqueViolators = FLintRuleViolation::AllRuleViolationViolators(RuleViolations); - for (const UObject* Violator : UniqueViolators) - { - TSharedPtr AssetJsonObject = MakeShareable(new FJsonObject); - TArray UniqueViolatorViolations = FLintRuleViolation::AllRuleViolationsWithViolator(RuleViolations, Violator); - - FAssetData AssetData; - if (UniqueViolatorViolations.Num() > 0) - { - UniqueViolatorViolations[0].PopulateAssetData(); - AssetData = UniqueViolatorViolations[0].ViolatorAssetData; - AssetJsonObject->SetStringField(TEXT("ViolatorAssetName"), AssetData.AssetName.ToString()); - AssetJsonObject->SetStringField(TEXT("ViolatorAssetPath"), AssetData.ObjectPath.ToString()); - AssetJsonObject->SetStringField(TEXT("ViolatorFullName"), AssetData.GetFullName()); - //@TODO: Thumbnail export? - - TArray> RuleViolationJsonObjects; - - for (const FLintRuleViolation& Violation : UniqueViolatorViolations) - { - ULintRule* LintRule = Violation.ViolatedRule->GetDefaultObject(); - check(LintRule != nullptr); - - TSharedPtr RuleJsonObject = MakeShareable(new FJsonObject); - RuleJsonObject->SetStringField(TEXT("RuleGroup"), LintRule->RuleGroup.ToString()); - RuleJsonObject->SetStringField(TEXT("RuleTitle"), LintRule->RuleTitle.ToString()); - RuleJsonObject->SetStringField(TEXT("RuleDesc"), LintRule->RuleDescription.ToString()); - RuleJsonObject->SetStringField(TEXT("RuleURL"), LintRule->RuleURL); - RuleJsonObject->SetNumberField(TEXT("RuleSeverity"), (int32)LintRule->RuleSeverity); - RuleJsonObject->SetStringField(TEXT("RuleRecommendedAction"), Violation.RecommendedAction.ToString()); - RuleViolationJsonObjects.Push(MakeShareable(new FJsonValueObject(RuleJsonObject))); - } - - AssetJsonObject->SetArrayField(TEXT("Violations"), RuleViolationJsonObjects); - } - - ViolatorJsonObjects.Add(MakeShareable(new FJsonValueObject(AssetJsonObject))); - - } - - // Save off our JSON to a string - RootJsonObject->SetArrayField(TEXT("Violators"), ViolatorJsonObjects); - FString JsonReport; - TSharedRef>> Writer = TJsonWriterFactory>::Create(&JsonReport); - FJsonSerializer::Serialize(RootJsonObject.ToSharedRef(), Writer); - - // write json file if requested - if (Switches.Contains(TEXT("json")) || ParamsMap.Contains(FString(TEXT("json")))) - { - FDateTime Now = FDateTime::Now(); - FString JsonOutputName = TEXT("lint-report-") + FDateTime::Now().ToString() + TEXT(".json"); - - const FString LintReportPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); - FString FullOutputPath = LintReportPath / JsonOutputName; - - if (ParamsMap.Contains(FString(TEXT("json")))) - { - const FString JsonOutputOverride = *ParamsMap.FindChecked(FString(TEXT("json"))); - if (FPaths::IsRelative(JsonOutputOverride)) - { - JsonOutputName = JsonOutputOverride; - FullOutputPath = LintReportPath / JsonOutputName; - } - else - { - FullOutputPath = JsonOutputOverride; - } - } - - FullOutputPath = FPaths::ConvertRelativePathToFull(FullOutputPath); - IFileManager::Get().MakeDirectory(*FPaths::GetPath(FullOutputPath), true); - - UE_LOG(LinterCommandlet, Display, TEXT("Exporting JSON report to %s"), *FullOutputPath); - if (FFileHelper::SaveStringToFile(JsonReport, *FullOutputPath)) - { - UE_LOG(LinterCommandlet, Display, TEXT("Exported JSON report successfully.")); - } - else - { - UE_LOG(LinterCommandlet, Error, TEXT("Failed to export JSON report. Aborting. Returning error code 1.")); - return 1; - } - } - - // write HTML report if requested - if (Switches.Contains(TEXT("html")) || ParamsMap.Contains(FString(TEXT("html")))) - { - FDateTime Now = FDateTime::Now(); - FString HtmlOutputName = TEXT("lint-report-") + FDateTime::Now().ToString() + TEXT(".html"); - - const FString LintReportPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); - FString FullOutputPath = LintReportPath / HtmlOutputName; - - if (ParamsMap.Contains(FString(TEXT("html")))) - { - const FString HtmlOutputOverride = *ParamsMap.FindChecked(TEXT("html")); - if (FPaths::IsRelative(HtmlOutputName)) - { - HtmlOutputName = HtmlOutputOverride; - FullOutputPath = LintReportPath / HtmlOutputName; - } - else - { - FullOutputPath = HtmlOutputOverride; - } - } - - FullOutputPath = FPaths::ConvertRelativePathToFull(FullOutputPath); - IFileManager::Get().MakeDirectory(*FPaths::GetPath(FullOutputPath), true); - UE_LOG(LinterCommandlet, Display, TEXT("Exporting HTML report to %s"), *FullOutputPath); - - FString TemplatePath = FPaths::Combine(*IPluginManager::Get().FindPlugin(TEXT("Linter"))->GetBaseDir(), TEXT("Resources"), TEXT("LintReportTemplate.html")); - UE_LOG(LinterCommandlet, Display, TEXT("Loading HTML report template from %s"), *TemplatePath); - - FString HTMLReport; - if (FFileHelper::LoadFileToString(HTMLReport, *TemplatePath)) - { - UE_LOG(LinterCommandlet, Display, TEXT("Loading HTML report template successfully.")); - - HTMLReport.ReplaceInline(TEXT("{% TITLE %}"), *FPaths::GetBaseFilename(FPaths::GetProjectFilePath())); - HTMLReport.ReplaceInline(TEXT("{% RESULTS %}"), *ResultsString); - HTMLReport.ReplaceInline(TEXT("{% LINT_REPORT %}"), *JsonReport); - } - else - { - UE_LOG(LinterCommandlet, Error, TEXT("Failed to load HTML report template.")); - return 1; - } - - if (FFileHelper::SaveStringToFile(HTMLReport, *FullOutputPath)) - { - UE_LOG(LinterCommandlet, Display, TEXT("Exported HTML report successfully.")); - } - else - { - UE_LOG(LinterCommandlet, Error, TEXT("Failed to export HTML report. Aborting. Returning error code 1.")); - return 1; - } - } - } - - if (NumErrors > 0 || Switches.Contains(TEXT("TreatWarningsAsErrors")) && NumWarnings > 0) - { - UE_LOG(LinterCommandlet, Display, TEXT("Lint completed with errors. Returning error code 2.")); - return 2; - } - - return 0; -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LinterContentBrowserExtensions.cpp b/Plugins/Linter/Source/Linter/Private/LinterContentBrowserExtensions.cpp deleted file mode 100644 index 099c20d..0000000 --- a/Plugins/Linter/Source/Linter/Private/LinterContentBrowserExtensions.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. -#include "LinterContentBrowserExtensions.h" -#include "Modules/ModuleManager.h" -#include "LevelEditor.h" -#include "Framework/MultiBox/MultiBoxBuilder.h" -#include "LinterStyle.h" -#include "LauncherPlatformModule.h" -#include "UnrealEdMisc.h" -#include "ContentBrowserModule.h" -#include "EditorStyleSet.h" -#include "Framework/MultiBox/MultiBoxExtender.h" -#include "Framework/Commands/UIAction.h" -#include "Delegates/IDelegateInstance.h" -#include "TooltipEditor/TooltipTool.h" - -#define LOCTEXT_NAMESPACE "Linter" -DEFINE_LOG_CATEGORY_STATIC(LinterContentBrowserExtensions, Log, All); - -void FLinterContentBrowserExtensions::InstallHooks(FLinterModule* LinterModule, FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle) -{ - struct Local - { - // Path extensions - - static TSharedRef OnExtendContentBrowserAssetSelectionMenu(const TArray& SelectedPaths) - { - TSharedRef Extender = MakeShared(); - Extender->AddMenuExtension( - "PathContextSourceControl", - EExtensionHook::After, - TSharedPtr(), - FMenuExtensionDelegate::CreateStatic(&Local::ContentBrowserExtenderFunc, SelectedPaths) - ); - return Extender; - } - - static void ContentBrowserExtenderFunc(FMenuBuilder& MenuBuilder, const TArray SelectedPaths) - { - MenuBuilder.BeginSection("LinterContentBrowserContext", LOCTEXT("CB_LinterHeader", "Linter")); - { - MenuBuilder.AddMenuEntry( - LOCTEXT("CB_ScanProjectWithLinter", "Scan with Linter"), - LOCTEXT("CB_ScanProjectWithLinter_Tooltip", "Scan project content with Linter"), - FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"), - FUIAction(FExecuteAction::CreateLambda([SelectedPaths]() - { - if (FLinterModule* lm = FModuleManager::GetModulePtr("Linter")) - { - if (lm != nullptr) - { - lm->SetDesiredLintPaths(SelectedPaths); - } - FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab")); - } - })), - NAME_None, - EUserInterfaceActionType::Button); - } - MenuBuilder.EndSection(); - } - - // Asset extensions - - static TSharedRef OnExtendAssetSelectionMenu(const TArray& SelectedAssets) - { - TSharedRef Extender = MakeShared(); - Extender->AddMenuExtension( - "CommonAssetActions", - EExtensionHook::After, - nullptr, - FMenuExtensionDelegate::CreateStatic(&Local::AssetExtenderFunc, SelectedAssets) - ); - return Extender; - } - - static void AssetExtenderFunc(FMenuBuilder& MenuBuilder, const TArray SelectedAssets) - { - MenuBuilder.BeginSection("LinterAssetContext", LOCTEXT("CB_LinterHeader", "Linter")); - { - - // Run through the assets to determine if any are blueprints - bool bAnyBlueprintsSelected = false; - for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt) - { - const FAssetData& Asset = *AssetIt; - // Cannot rename redirectors or classes or cooked packages - if (!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly)) - { - if (Asset.GetClass()->IsChildOf(UBlueprint::StaticClass())) - { - bAnyBlueprintsSelected = true; - break; - } - } - } - - // If we have blueprints selected, enable blueprint tools - if (bAnyBlueprintsSelected) - { - // Add Tooltip Editor - MenuBuilder.AddMenuEntry( - LOCTEXT("CB_EditBlueprintTooltips", "Edit Blueprint Tooltips (Experimental)"), - LOCTEXT("CB_EditBlueprintTooltips_Tooltip", "Edit selected blueprints' templates definitions"), - FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"), - FUIAction(FExecuteAction::CreateLambda([SelectedAssets]() - { - UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Opening Tooltip Tool window.")); - FTooltipTool AssetDlg(SelectedAssets); - if (AssetDlg.ShowModal() == FTooltipTool::Confirm) - { - UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Tooltip Tool did the thing.")); - } - })), - NAME_None, - EUserInterfaceActionType::Button); - } - - // Run through the assets to see if any can be renamed - bool bAnyAssetCanBeRenamed = false; - for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt) - { - const FAssetData& Asset = *AssetIt; - // Cannot rename redirectors or classes or cooked packages - if (!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly)) - { - bAnyAssetCanBeRenamed = true; - break; - } - } - - if (bAnyAssetCanBeRenamed) - { - // Add Tooltip Editor - MenuBuilder.AddMenuEntry( - LOCTEXT("CB_BatchRenameAssets", "Batch Rename Assets (Experimental)"), - LOCTEXT("CB_BatchRenameAssets_Tooltip", "Perform a bulk rename operation on all of the selected assets"), - FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"), - FUIAction(FExecuteAction::CreateLambda([SelectedAssets]() - { - UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Starting batch rename.")); - FDlgBatchRenameTool AssetDlg(SelectedAssets); - AssetDlg.ShowModal(); - })), - NAME_None, - EUserInterfaceActionType::Button); - } - } - MenuBuilder.EndSection(); - } - }; - - FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")); - - // Path view extenders - TArray& CBMenuPathExtenderDelegates = ContentBrowserModule.GetAllPathViewContextMenuExtenders(); - CBMenuPathExtenderDelegates.Add(FContentBrowserMenuExtender_SelectedPaths::CreateStatic(&Local::OnExtendContentBrowserAssetSelectionMenu)); - *pContentBrowserExtenderDelegateHandle = CBMenuPathExtenderDelegates.Last().GetHandle(); - - // Asset extenders - TArray& CBMenuAssetExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders(); - CBMenuAssetExtenderDelegates.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&Local::OnExtendAssetSelectionMenu)); - *pAssetExtenderDelegateHandle = CBMenuAssetExtenderDelegates.Last().GetHandle(); -} - -void FLinterContentBrowserExtensions::RemoveHooks(FLinterModule* LinterModule, FDelegateHandle* pContentBrowserExtenderDelegateHandle, FDelegateHandle* pAssetExtenderDelegateHandle) -{ - if (FModuleManager::Get().IsModuleLoaded("ContentBrowser")) - { - FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked(TEXT("ContentBrowser")); - - // Path view extenders - TArray& CBMenuExtenderDelegates = ContentBrowserModule.GetAllAssetContextMenuExtenders(); - CBMenuExtenderDelegates.RemoveAll([pContentBrowserExtenderDelegateHandle](const FContentBrowserMenuExtender_SelectedPaths & Delegate) { return Delegate.GetHandle() == *pContentBrowserExtenderDelegateHandle; }); - - // Asset extenders - TArray& CBMenuAssetExtenderDelegates = ContentBrowserModule.GetAllAssetViewContextMenuExtenders(); - CBMenuAssetExtenderDelegates.RemoveAll([pAssetExtenderDelegateHandle](const FContentBrowserMenuExtender_SelectedAssets & Delegate) { return Delegate.GetHandle() == *pAssetExtenderDelegateHandle; }); - } -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LinterNamingConvention.cpp b/Plugins/Linter/Source/Linter/Private/LinterNamingConvention.cpp deleted file mode 100644 index 8ed5a9c..0000000 --- a/Plugins/Linter/Source/Linter/Private/LinterNamingConvention.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "LinterNamingConvention.h" -#include "DetailLayoutBuilder.h" -#include "PropertyCustomizationHelpers.h" -#include "Templates/SharedPointer.h" -#include "DetailCategoryBuilder.h" -#include "IDetailChildrenBuilder.h" - -TSharedRef FLinterNamingConventionDetails::MakeInstance() -{ - return MakeShareable(new FLinterNamingConventionDetails()); -} - -void FLinterNamingConventionDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) -{ - // Edit the Conventions category - IDetailCategoryBuilder& DetailCategory = DetailBuilder.EditCategory("Conventions"); - TSharedRef NamingConventionsProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULinterNamingConvention, ClassNamingConventions), ULinterNamingConvention::StaticClass()); - - TSharedRef ConventionsPropertyBuilder = MakeShareable(new FDetailArrayBuilder(NamingConventionsProperty)); - ConventionsPropertyBuilder->OnGenerateArrayElementWidget(FOnGenerateArrayElementWidget::CreateSP(this, &FLinterNamingConventionDetails::OnGenerateElementForDetails, &DetailBuilder)); - - - DetailCategory.AddCustomBuilder(ConventionsPropertyBuilder); -} - -void FLinterNamingConventionDetails::OnGenerateElementForDetails(TSharedRef StructProperty, int32 ElementIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout) -{ - ChildrenBuilder.AddCustomRow(FText::GetEmpty()) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(SProperty, StructProperty->GetChildHandle("SoftClassPtr")) - .ShouldDisplayName(false) - ] - + SHorizontalBox::Slot() - .FillWidth(0.25f) - [ - SNew(SProperty, StructProperty->GetChildHandle("Variant")) - ] - + SHorizontalBox::Slot() - .FillWidth(0.25f) - [ - SNew(SProperty, StructProperty->GetChildHandle("Prefix")) - ] - + SHorizontalBox::Slot() - .FillWidth(0.25f) - [ - SNew(SProperty, StructProperty->GetChildHandle("Suffix")) - ] - ]; -} - -ULinterNamingConvention::ULinterNamingConvention(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - ClassNamingConventions = TArray(); -} - -TArray ULinterNamingConvention::GetNamingConventionsForClassVariant(TSoftClassPtr Class, FName Variant /*= NAME_None*/) const -{ - TArray NamingConventionList; - - UClass* searchClass = Class.Get(); - while (NamingConventionList.Num() == 0 && searchClass != nullptr) - { - NamingConventionList = ClassNamingConventions.FilterByPredicate([searchClass, Variant](const FLinterNamingConventionInfo& Info) - { - return (Info.SoftClassPtr.Get() == searchClass && Info.Variant == Variant); - }); - - // Abort if we try to go above UObject - if (searchClass == UObject::StaticClass()) - { - break; - } - - // @HACK: Editor UI won't allow us to select the UObject class in some cases - if (searchClass == UAnyObject_LinterDummyClass::StaticClass()) - { - searchClass = UObject::StaticClass(); - continue; - } - - // Load our parent class in case we failed to get naming conventions - searchClass = searchClass->GetSuperClass(); - } - - return NamingConventionList; -} - -void ULinterNamingConvention::SortConventions() -{ - ClassNamingConventions.Sort([](const FLinterNamingConventionInfo& A, const FLinterNamingConventionInfo& B) - { - if (A.SoftClassPtr.GetAssetName() < B.SoftClassPtr.GetAssetName()) - { - return true; - } - - if (A.SoftClassPtr.GetAssetName() == B.SoftClassPtr.GetAssetName()) - { - int32 sort = A.Variant.Compare(B.Variant); - if (sort < 0) - { - return true; - } - - if (sort == 0) - { - sort = A.Prefix.Compare(B.Prefix); - if (sort < 0) - { - return true; - } - - if (sort == 0) - { - sort = A.Suffix.Compare(B.Suffix); - if (sort <= 0) - { - return true; - } - return false; - } - - return false; - } - - return false; - } - - return false; - }); -} diff --git a/Plugins/Linter/Source/Linter/Private/LinterSettings.cpp b/Plugins/Linter/Source/Linter/Private/LinterSettings.cpp deleted file mode 100644 index 86939aa..0000000 --- a/Plugins/Linter/Source/Linter/Private/LinterSettings.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. - -#include "LinterSettings.h" -#include "UObject/ConstructorHelpers.h" -#include "LintRuleSet.h" - - -ULinterSettings::ULinterSettings(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - if (DefaultLintRuleSet.IsNull()) - { - static ConstructorHelpers::FObjectFinder DefaultMarketplaceRuleSetRef(TEXT("LintRuleSet'/Linter/MarketplaceLinter/MarketplaceLintRuleSet.MarketplaceLintRuleSet'")); - DefaultLintRuleSet = DefaultMarketplaceRuleSetRef.Object; - } -} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/LinterStyle.cpp b/Plugins/Linter/Source/Linter/Private/LinterStyle.cpp deleted file mode 100644 index 6e1480e..0000000 --- a/Plugins/Linter/Source/Linter/Private/LinterStyle.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. - -#include "LinterStyle.h" -#include "Styling/SlateTypes.h" -#include "Styling/SlateStyle.h" -#include "Interfaces/IPluginManager.h" -#include "Styling/SlateStyleRegistry.h" -#include "EditorStyleSet.h" - -#define IMAGE_PLUGIN_BRUSH( RelativePath, ... ) FSlateImageBrush( FLinterStyle::InContent( RelativePath, ".png" ), __VA_ARGS__ ) -#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) -#define BOX_BRUSH(RelativePath, ...) FSlateBoxBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) -#define TTF_FONT(RelativePath, ...) FSlateFontInfo(StyleSet->RootToContentDir(RelativePath, TEXT(".ttf")), __VA_ARGS__) -#define TTF_CORE_FONT(RelativePath, ...) FSlateFontInfo(StyleSet->RootToCoreContentDir(RelativePath, TEXT(".ttf") ), __VA_ARGS__) - -FString FLinterStyle::InContent(const FString& RelativePath, const ANSICHAR* Extension) -{ - static FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("Linter"))->GetContentDir(); - return (ContentDir / RelativePath) + Extension; -} - -TSharedPtr< FSlateStyleSet > FLinterStyle::StyleSet = nullptr; -TSharedPtr< class ISlateStyle > FLinterStyle::Get() { return StyleSet; } - -FName FLinterStyle::GetStyleSetName() -{ - static FName LinterStyleName(TEXT("LinterStyle")); - return LinterStyleName; -} - -void FLinterStyle::Initialize() -{ - // Const icon sizes - const FVector2D Icon8x8(8.0f, 8.0f); - const FVector2D Icon14x14(14.0f, 14.0f); - const FVector2D Icon16x16(16.0f, 16.0f); - const FVector2D Icon20x20(20.0f, 20.0f); - const FVector2D Icon40x40(40.0f, 40.0f); - const FVector2D Icon64x64(64.0f, 64.0f); - const FVector2D Icon128x128(128.0f, 128.0f); - - // Only register once - if (StyleSet.IsValid()) - { - return; - } - - StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName())); - StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate")); - StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); - - // Asset actions - { - StyleSet->Set("AssetActions.RunLinter", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); - StyleSet->Set("AssetActions.BatchRename", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); - StyleSet->Set("AssetActions.TooltipTool", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); - - // Toolbar Button Icons - StyleSet->Set("Linter.Toolbar.Icon", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); - - // Report Images - StyleSet->Set("Linter.Step.Unknown", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Unknown_64px", Icon64x64)); - StyleSet->Set("Linter.Step.Error", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Fail_64px", Icon64x64)); - StyleSet->Set("Linter.Step.Good", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Good_64px", Icon64x64)); - StyleSet->Set("Linter.Step.Working", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Working_64px", Icon64x64)); - StyleSet->Set("Linter.Step.Warning", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Warning_64px", Icon64x64)); - - StyleSet->Set("Linter.Report.Link", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Help_Documentation_16x"), Icon16x16)); - StyleSet->Set("Linter.Report.Warning", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Warning_14x"), Icon14x14)); - StyleSet->Set("Linter.Report.Error", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Error_14x"), Icon14x14)); - - StyleSet->Set("Linter.Report.Info", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Info_14x"), Icon20x20)); - - - StyleSet->Set("Linter.Step.BuildLighting.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MapCheck_64x"), Icon64x64)); - StyleSet->Set("Linter.Step.Package.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_AddContent_64x"), Icon64x64)); - StyleSet->Set("Linter.Step.SaveAll.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_file_saveall_64x"), Icon64x64)); - StyleSet->Set("Linter.Step.FixupRedirects.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_tab_Toolbars_64x"), Icon64x64)); - - // PaCK Sizing - StyleSet->Set("Linter.Padding", 2.0f); - - // PaCK Fonts - const FTextBlockStyle NormalText = FEditorStyle::GetWidgetStyle("NormalText"); - - FTextBlockStyle NameText = FTextBlockStyle(NormalText) - .SetColorAndOpacity(FLinearColor(0.9f, 0.9f, 0.9f)); - { - NameText.Font.Size = 14; - StyleSet->Set("Linter.Report.AssetName", NameText); - } - - FTextBlockStyle RuleTitleText = FTextBlockStyle(NormalText) - .SetColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f)); - { - RuleTitleText.Font.Size = 12; - StyleSet->Set("Linter.Report.RuleTitle", RuleTitleText); - } - - FTextBlockStyle DescriptionText = FTextBlockStyle(NormalText) - .SetColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f)); - { - DescriptionText.Font.Size = 10; - StyleSet->Set("Linter.Report.DescriptionText", DescriptionText); - } - } - - FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); -}; - -#undef IMAGE_PLUGIN_BRUSH -#undef IMAGE_BRUSH -#undef BOX_BRUSH -#undef TTF_FONT -#undef TTF_CORE_FONT - -void FLinterStyle::Shutdown() -{ - if (StyleSet.IsValid()) - { - FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); - ensure(StyleSet.IsUnique()); - StyleSet.Reset(); - } -} diff --git a/Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp b/Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp deleted file mode 100644 index 1f25f6b..0000000 --- a/Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "TooltipEditor/TooltipStringHelper.h" - -FText FTooltipStringHelper::ParseFunctionRawTooltipGetDescription(FString RawTooltip, bool bRemoveNewlines/* = false*/) -{ - if (RawTooltip.Len() == 0) - { - return FText::GetEmpty(); - } - - TArray Lines; - RawTooltip.ParseIntoArrayLines(Lines); - - FString Description; - - for (FString Line : Lines) - { - if (Line.StartsWith(TEXT("@param"))) - { - break; - } - else if (Line.StartsWith(TEXT("@return"))) //@return is assumed to always be last - { - break; - } - else - { - if (Description.Len() > 0) - { - Description.Append(TEXT("\n")); - } - Description.Append(Line); - } - } - - if (bRemoveNewlines) - { - Description.ReplaceInline(TEXT("\r"), TEXT("")); - Description.ReplaceInline(TEXT("\n"), TEXT(" ")); - } - - return FText::FromString(Description); -} - -bool FTooltipStringHelper::ParseFunctionRawTooltip(FString RawTooltip, FText& OutFunctionDescription, TArray>& Inputs, TArray>& Outputs, FText& OutReturnText) -{ - if (RawTooltip.Len() == 0) - { - return false; - } - - TArray Lines; - RawTooltip.ParseIntoArrayLines(Lines); - - FString Description; - bool bParsingDescription = true; - - FText CurrentArgumentName; - FString CurrentArgumentTooltip; - bool bCurrentArgumentIsInput = true; - - for (FString Line : Lines) - { - if (Line.StartsWith(TEXT("@param"))) - { - if (!bParsingDescription) - { - if (bCurrentArgumentIsInput) - { - FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); - } - else - { - FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); - } - - CurrentArgumentName = FText::GetEmpty(); - CurrentArgumentTooltip.Empty(); - } - - bParsingDescription = false; - bCurrentArgumentIsInput = true; - - Line = Line.RightChop(6); - Line.TrimStartAndEndInline(); - - if (Line.StartsWith("[out]")) - { - bCurrentArgumentIsInput = false; - Line = Line.RightChop(5); - Line.TrimStartAndEndInline(); - } - - Line = Line.ConvertTabsToSpaces(4); - FString ArgumentName; - FString ArgumentTooltip; - if (Line.Split(TEXT(" "), &ArgumentName, &CurrentArgumentTooltip)) - { - CurrentArgumentName = FText::FromString(ArgumentName); - CurrentArgumentTooltip.TrimStartAndEndInline(); - } - } - else if (Line.StartsWith(TEXT("@return"))) //@return is assumed to always be last - { - if (!bParsingDescription) - { - if (bCurrentArgumentIsInput) - { - FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); - } - else - { - FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); - } - - CurrentArgumentName = FText::GetEmpty(); - CurrentArgumentTooltip.Empty(); - } - - Line = Line.RightChop(7); - Line.TrimStartAndEndInline(); - OutReturnText = FText::FromString(Line); - } - else - { - if (bParsingDescription) - { - if (Description.Len() > 0) - { - Description.Append(TEXT("\n")); - } - Description.Append(Line); - } - else - { - Line.TrimStartAndEndInline(); - CurrentArgumentTooltip.AppendChar(TEXT(' ')); - CurrentArgumentTooltip.Append(Line); - } - } - } - - if (!CurrentArgumentName.IsEmpty()) - { - if (!bParsingDescription) - { - if (bCurrentArgumentIsInput) - { - FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); - } - else - { - FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); - } - - CurrentArgumentName = FText::GetEmpty(); - CurrentArgumentTooltip.Empty(); - } - } - - OutFunctionDescription = FText::FromString(Description); - return true; -} - -FString FTooltipStringHelper::ConvertTooltipDataToRawTooltip(FText FunctionDescription, TArray> Inputs, TArray> Outputs) -{ - FString RawTooltip = FunctionDescription.ToString(); - for (TSharedPtr Arg : Inputs) - { - RawTooltip.Append(FString::Printf(TEXT("\n@param %s %s\t\t\t%s"), TEXT(" "), *Arg->ArgumentName.ToString(), *Arg->Tooltip.ToString())); - } - - if (Outputs.Num() == 1) - { - RawTooltip.Append(FString::Printf(TEXT("\n@return %s"), *Outputs[0]->Tooltip.ToString())); - } - else - { - for (TSharedPtr Arg : Outputs) - { - RawTooltip.Append(FString::Printf(TEXT("\n@param %s %s\t\t\t%s"), TEXT("[out]"), *Arg->ArgumentName.ToString(), *Arg->Tooltip.ToString())); - } - } - - return RawTooltip; -} - -bool FTooltipStringHelper::FindAndUpdateArgumentTooltip(FText ArgumentName, FText Tooltip, TArray>& Arguments) -{ - TSharedPtr* FuncArg = Arguments.FindByPredicate([&](TSharedPtr Arg){ return Arg->ArgumentName.EqualTo(ArgumentName, ETextComparisonLevel::Quinary);}); - if (FuncArg != nullptr) - { - (*FuncArg)->Tooltip = Tooltip; - return true; - } - else - { - return false; - } -} diff --git a/Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipTool.cpp b/Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipTool.cpp deleted file mode 100644 index 1dffd40..0000000 --- a/Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipTool.cpp +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright 2016 Gamemakin LLC. All Rights Reserved. - -#include "TooltipEditor/TooltipTool.h" - -#include "Framework/Application/SlateApplication.h" -#include "Widgets/Layout/SBox.h" -#include "Widgets/Layout/SHeader.h" -#include "Widgets/Images/SImage.h" -#include "Framework/Text/TextLayout.h" -#include "Widgets/Text/STextBlock.h" -#include "Widgets/Layout/SScrollBox.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "Widgets/Input/SButton.h" -#include "Types/SlateEnums.h" -#include "Editor.h" -#include "EdGraphSchema_K2.h" -#include "Kismet2/BlueprintEditorUtils.h" -#include "FileHelpers.h" - -#define LOCTEXT_NAMESPACE "LinterTooltipTool" - -FTooltipTool::FTooltipTool(const TArray Assets) -{ - for (auto Asset : Assets) - { - if (Asset.GetClass()->IsChildOf(UBlueprint::StaticClass())) - { - BlueprintsInternal.Push(Asset); - } - } - - for (int32 i = 0; i < BlueprintsInternal.Num(); ++i) - { - Blueprints.Push(TSharedPtr(&BlueprintsInternal[i])); - } - - if (FSlateApplication::IsInitialized()) - { - DialogWindow = SNew(SWindow) - .Title(LOCTEXT("TooltipToolDlgTitle", "Blueprint Member Tooltip Tool")) - .SupportsMinimize(false).SupportsMaximize(false) - .SaneWindowPlacement(true) - .AutoCenter(EAutoCenter::PreferredWorkArea) - .MinWidth(400.0f) - .MaxWidth(400.0f) - .SizingRule(ESizingRule::Autosized); - - TSharedPtr DialogWrapper = - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(4.0f) - [ - SAssignNew(DialogWidget, STooltipTool) - .ParentWindow(DialogWindow) - .Blueprints(Blueprints) - ]; - - DialogWindow->SetContent(DialogWrapper.ToSharedRef()); - } -} - -FTooltipTool::EResult FTooltipTool::ShowModal() -{ - //Show Dialog - GEditor->EditorAddModalWindow(DialogWindow.ToSharedRef()); - EResult UserResponse = (EResult)DialogWidget->GetUserResponse(); - - if (UserResponse == EResult::Confirm) - { - - - } - return UserResponse; -} - -void STooltipTool::Construct(const FArguments& InArgs) -{ - UserResponse = FTooltipTool::Cancel; - ParentWindow = InArgs._ParentWindow.Get(); - Blueprints = InArgs._Blueprints; - check(Blueprints.IsSet() && Blueprints.Get().Num() > 0); - - this->ChildSlot[ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 0.0f) - [ - SNew(SHeader) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolBlueprintHeader", "Blueprint")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SAssignNew(BlueprintComboBox, SComboBox>) - .OptionsSource(&Blueprints.Get()) - .InitiallySelectedItem(Blueprints.Get()[0]) - .OnGenerateWidget_Lambda( - [](TSharedPtr Item) - { - return SNew(STextBlock) - .Text(FText::FromName(Item->AssetName)) - .ToolTipText(FText::FromString(Item->GetFullName())); - }) - .OnSelectionChanged_Lambda( - [&](TSharedPtr Item, ESelectInfo::Type SelectInfo) - { - VariableTooltipEditableTextBox->SetEnabled(false); - RebuildMemberList(); - }) - [ - SNew(STextBlock) - .Text(this, &STooltipTool::GetSelectedBlueprintText) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(SButton) - .OnClicked_Lambda( - [&] { - int32 Index = Blueprints.Get().Find(BlueprintComboBox->GetSelectedItem()); - Index = (Blueprints.Get().Num() + Index - 1) % Blueprints.Get().Num(); - BlueprintComboBox->SetSelectedItem(Blueprints.Get()[Index]); - return FReply::Handled(); - }) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolBlueprintsPrevious", "Previous")) - ] - ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(SButton) - .OnClicked_Lambda( - [&] { - int32 Index = Blueprints.Get().Find(BlueprintComboBox->GetSelectedItem()); - Index = (Index + 1) % Blueprints.Get().Num(); - BlueprintComboBox->SetSelectedItem(Blueprints.Get()[Index]); - return FReply::Handled(); - }) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolBlueprintsNext", "Next")) - ] - ] - - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SButton) - .OnClicked_Lambda( - [&]{ - // Save package here if SCC is enabled because the user can use SCC to revert a change - TArray OutermostPackagesToSave; - for (auto Asset : Blueprints.Get()) - { - OutermostPackagesToSave.Add(Asset->GetPackage()); - } - FEditorFileUtils::PromptForCheckoutAndSave(OutermostPackagesToSave, true, false); - return FReply::Handled(); - }) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolBlueprintsSaveButtonLabel", "Save All Blueprints")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SExpandableArea) - .InitiallyCollapsed(true) - .AreaTitle(LOCTEXT("TooltipToolVariablesExpandableTitle", "Variables")) - .BodyContent() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SBox) - .HeightOverride(100.0f) - [ - SNew(SBorder) - .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) - .Padding(0.0f) - [ - SNew(SScrollBox) - .ScrollBarAlwaysVisible(true) - + SScrollBox::Slot() - [ - SAssignNew(MemberListView, SListView>) - .SelectionMode(ESelectionMode::Single) - .ListItemsSource(&Members) - .OnGenerateRow_Lambda( - [](TSharedPtr Item, const TSharedRef& OwnerTable) - { - return SNew(STableRow>, OwnerTable) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("Icons.Error"))) - .Visibility_Lambda([Item] {return Item.IsValid() ? (Item->HasMetaData(FBlueprintMetadata::MD_Tooltip) && Item->GetMetaData(FBlueprintMetadata::MD_Tooltip).Len() > 0 ? EVisibility::Collapsed : EVisibility::HitTestInvisible) : EVisibility::Collapsed; }) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(STextBlock) - .Text(FText::FromString(Item->FriendlyName)) - ] - + SHorizontalBox::Slot() - .FillWidth(1.0f) - [ - SNew(STextBlock) - .Text(UEdGraphSchema_K2::TypeToText(Item->VarType)) - .Justification(ETextJustify::Right) - ] - ]; - }) - .OnSelectionChanged_Lambda( - [&](TSharedPtr Item, ESelectInfo::Type SelectInfo) - { - if (!Item.IsValid()) - { - VariableTooltipEditableTextBox->SetEnabled(false); - VariableTooltipEditableTextBox->SetText(FText::GetEmpty()); - return; - } - - VariableTooltipEditableTextBox->SetEnabled(true); - if (Item->HasMetaData(FBlueprintMetadata::MD_Tooltip)) - { - VariableTooltipEditableTextBox->SetText(FText::FromString(Item->GetMetaData(FBlueprintMetadata::MD_Tooltip))); - } - else - { - VariableTooltipEditableTextBox->SetText(FText::GetEmpty()); - } - }) - ] - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SHeader) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolTooltipHeader", "Tooltip")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SBox) - .HeightOverride(100.0f) - [ - SAssignNew(VariableTooltipEditableTextBox, SMultiLineEditableTextBox) - .AutoWrapText(true) - .IsEnabled(false) - .OnTextChanged_Lambda( - [&](const FText& NewText) - { - if (CommitOnTextChangeCheckBox->IsChecked()) - { - UpdateVariableTooltipText(NewText); - } - }) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SAssignNew(CommitTextButton, SButton) - .OnClicked_Lambda([&] { /**UpdateVariableTooltipText(TooltipEditableTextBox->GetText());**/ return FReply::Handled(); }) - .Visibility_Lambda([&] {return CommitOnTextChangeCheckBox->IsChecked() ? EVisibility::Collapsed : EVisibility::Visible; }) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolCommitTextButtonLabel", "Commit Text")) - ] - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SExpandableArea) - .InitiallyCollapsed(true) - .AreaTitle(LOCTEXT("TooltipToolFunctionExpandableTitle", "Functions")) - .BodyContent() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SBox) - .HeightOverride(100.0f) - [ - SNew(SBorder) - .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) - .Padding(0.0f) - [ - SNew(SScrollBox) - .ScrollBarAlwaysVisible(true) - + SScrollBox::Slot() - [ - SAssignNew(FunctionListView, SListView>) - .SelectionMode(ESelectionMode::Single) - .ListItemsSource(&FunctionPointers) - .OnGenerateRow_Lambda( - [](TSharedPtr Item, const TSharedRef& OwnerTable) - { - return SNew(STableRow>, OwnerTable) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("Icons.Error"))) - .Visibility_Lambda([Item] {return (Item.IsValid() && Item->FunctionEntryNode != nullptr&& !Item->FunctionEntryNode->MetaData.ToolTip.IsEmptyOrWhitespace()) ? EVisibility::Collapsed : EVisibility::Visible; }) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(STextBlock) - .Text(FText::FromName(Item->FunctionName)) - ] - ]; - }) - .OnSelectionChanged_Lambda( - [&](TSharedPtr Item, ESelectInfo::Type SelectInfo) - { - FunctionArgumentDescriptions.Empty(); - FunctionOutputDescriptions.Empty(); - - if (!Item.IsValid()) - { - FunctionArgumentListView->RebuildList(); - FunctionOutputListView->RebuildList(); - - FunctionDescriptionTooltipBox->SetEnabled(false); - FunctionArgumentListView->SetEnabled(false); - FunctionOutputListView->SetEnabled(false); - FunctionDescriptionTooltipBox->SetText(FText::GetEmpty()); - - return; - } - - check(Item->FunctionEntryNode); - TArray InputPins = Item->FunctionEntryNode->GetAllPins(); - for (UEdGraphPin* Pin : InputPins) - { - if (Pin->Direction == EEdGraphPinDirection::EGPD_Output) - { - if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec) - { - FunctionArgumentDescriptions.Add(MakeShared(FText::FromString(Pin->GetName()), FText::GetEmpty(), UEdGraphSchema_K2::TypeToText(Pin->PinType))); - } - } - } - - if (Item->FunctionResultNode != nullptr) - { - TArray OutputPins = Item->FunctionResultNode->GetAllPins(); - for (UEdGraphPin* Pin : OutputPins) - { - if (Pin->Direction == EEdGraphPinDirection::EGPD_Input) - { - if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec) - { - FunctionOutputDescriptions.Add(MakeShared(FText::FromString(Pin->GetName()), FText::GetEmpty(), UEdGraphSchema_K2::TypeToText(Pin->PinType))); - } - } - } - } - - FunctionDescriptionTooltipBox->SetEnabled(true); - FunctionArgumentListView->SetEnabled(true); - FunctionOutputListView->SetEnabled(true); - - FText FunctionDescription; - FText ReturnText; - - if (!FTooltipStringHelper::ParseFunctionRawTooltip(Item->FunctionEntryNode->MetaData.ToolTip.ToString(), CurrentFunctionDescription, FunctionArgumentDescriptions, FunctionOutputDescriptions, ReturnText)) - { - CurrentFunctionDescription = FText::GetEmpty(); - } - - if (!ReturnText.IsEmptyOrWhitespace() && FunctionOutputDescriptions.Num() > 0) - { - FunctionOutputDescriptions[FunctionOutputDescriptions.Num() - 1]->Tooltip = ReturnText; - } - - FunctionArgumentListView->RebuildList(); - FunctionOutputListView->RebuildList(); - - FunctionDescriptionTooltipBox->SetText(CurrentFunctionDescription); - - }) - ] - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SHeader) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolFunctionDesc", "Description")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SBox) - .HeightOverride(60.0f) - [ - SAssignNew(FunctionDescriptionTooltipBox, SMultiLineEditableTextBox) - .AutoWrapText(true) - .IsEnabled(false) - .OnTextChanged_Lambda( - [&](const FText& NewText) - { - if (CommitOnTextChangeCheckBox->IsChecked()) - { - if (!NewText.EqualTo(CurrentFunctionDescription)) - { - UpdateCurrentFunctionTooltipText(); - } - - } - }) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SHeader) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolFunctionInputs", "Inputs")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SBox) - .MinDesiredHeight(20.0f) - .MaxDesiredHeight(100.0f) - [ - SNew(SBorder) - .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) - .Padding(0.0f) - [ - SNew(SScrollBox) - .ScrollBarAlwaysVisible(true) - + SScrollBox::Slot() - [ - SAssignNew(FunctionArgumentListView, SListView>) - .SelectionMode(ESelectionMode::None) - .ListItemsSource(&FunctionArgumentDescriptions) - .OnGenerateRow_Lambda( - [&](TSharedPtr Item, const TSharedRef& OwnerTable) - { - return SNew(STableRow>, OwnerTable) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("Icons.Error"))) - .Visibility_Lambda([Item] {return (Item.IsValid() && !Item->Tooltip.IsEmptyOrWhitespace()) ? EVisibility::Collapsed : EVisibility::Visible; }) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(STextBlock) - .Text(FText::FromString(FString::Printf(TEXT("%s (%s)"), *Item->ArgumentName.ToString(), *Item->ArgumentType.ToString()))) - ] - ] - + SVerticalBox::Slot() - .Padding(0.0f, 4.0f, 0.0f, 4.0f) - [ - SNew(SEditableTextBox) - .Text(Item->Tooltip) - .OnTextChanged_Lambda( - [&](const FText& NewText) - { - if (CommitOnTextChangeCheckBox->IsChecked()) - { - UpdateCurrentFunctionTooltipText(); - } - }) - ] - ]; - }) - ] - ] - ] - ] - - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SHeader) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolFunctionOutputs", "Outputs")) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SBox) - .MinDesiredHeight(20.0f) - .MaxDesiredHeight(100.0f) - [ - SNew(SBorder) - .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) - .Padding(0.0f) - [ - SNew(SScrollBox) - .ScrollBarAlwaysVisible(true) - + SScrollBox::Slot() - [ - SAssignNew(FunctionOutputListView, SListView>) - .SelectionMode(ESelectionMode::None) - .ListItemsSource(&FunctionOutputDescriptions) - .OnGenerateRow_Lambda( - [&](TSharedPtr Item, const TSharedRef& OwnerTable) - { - return SNew(STableRow>, OwnerTable) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SImage) - .Image(FEditorStyle::GetBrush(TEXT("Icons.Error"))) - .Visibility_Lambda([Item] {return (Item.IsValid() && !Item->Tooltip.IsEmptyOrWhitespace()) ? EVisibility::Collapsed : EVisibility::Visible; }) - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(STextBlock) - .Text(FText::FromString(FString::Printf(TEXT("%s (%s)"), *Item->ArgumentName.ToString(), *Item->ArgumentType.ToString()))) - ] - ] - + SVerticalBox::Slot() - .Padding(0.0f, 4.0f, 0.0f, 4.0f) - [ - SNew(SEditableTextBox) - .Text(Item->Tooltip) - .OnTextChanged_Lambda( - [&](const FText& NewText) - { - if (CommitOnTextChangeCheckBox->IsChecked()) - { - UpdateCurrentFunctionTooltipText(); - } - }) - ] - ]; - }) - ] - ] - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SAssignNew(CommitTextButton, SButton) - .OnClicked_Lambda([&] { UpdateVariableTooltipText(VariableTooltipEditableTextBox->GetText()); return FReply::Handled(); }) - .Visibility_Lambda([&] {return CommitOnTextChangeCheckBox->IsChecked() ? EVisibility::Collapsed : EVisibility::Visible; }) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolCommitTextButtonLabel", "Commit Text")) - ] - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 4.0f) - [ - SNew(SSeparator) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(8.0f, 4.0f, 8.0f, 0.0f) - [ - SNew(SExpandableArea) - .InitiallyCollapsed(true) - .AreaTitle(LOCTEXT("TooltipToolBehaviorExpandableTitle", "Tool Behavior")) - .BodyContent() - [ - SAssignNew(CommitOnTextChangeCheckBox, SCheckBox) - .IsChecked(ECheckBoxState::Checked) - [ - SNew(STextBlock) - .Text(LOCTEXT("TooltipToolCommitOnChangeLabel", "Commit Tooltip on Text Change")) - ] - ] - ] - ]; - - RebuildMemberList(); -} - -FTooltipTool::EResult STooltipTool::GetUserResponse() const -{ - return UserResponse; -} - -FReply STooltipTool::OnButtonClick(FTooltipTool::EResult ButtonID) -{ - ParentWindow->RequestDestroyWindow(); - UserResponse = ButtonID; - - return FReply::Handled(); -} - -FText STooltipTool::GetSelectedBlueprintText() const -{ - if (BlueprintComboBox->GetSelectedItem().IsValid()) - { - return FText::FromName(BlueprintComboBox->GetSelectedItem().Get()->AssetName); - } - - return FText::FromString(TEXT("No Blueprint Selected.")); -} - -void STooltipTool::UpdateVariableTooltipText(const FText& NewText) -{ - // Early out if text box isn't enabled - if (!VariableTooltipEditableTextBox->IsEnabled()) - { - return; - } - - TArray> SelectedMembers; - MemberListView->GetSelectedItems(SelectedMembers); - if (SelectedMembers.Num() == 1) - { - TSharedPtr BlueprintAsset = BlueprintComboBox->GetSelectedItem(); - UBlueprint* Blueprint = CastChecked(BlueprintAsset->GetAsset()); - SelectedMembers[0]->SetMetaData(FBlueprintMetadata::MD_Tooltip, NewText.ToString()); - FBlueprintEditorUtils::SetBlueprintVariableMetaData(Blueprint, SelectedMembers[0]->VarName, nullptr, FBlueprintMetadata::MD_Tooltip, NewText.ToString()); - } - else - { - check(false); - } -} - -void STooltipTool::UpdateCurrentFunctionTooltipText() -{ - TArray> SelectedFunctions = FunctionListView->GetSelectedItems(); - - if (SelectedFunctions.Num() == 0) - { - return; - } - - check(SelectedFunctions.Num() == 1); //If anything is selected, only one thing should be selected. - - for (int32 i = 0; i < FunctionArgumentDescriptions.Num(); i++) - { - // @TODO: Don't do this - TSharedRef Child = FunctionArgumentListView->WidgetFromItem(FunctionArgumentDescriptions[i])->GetContent()->GetChildren()->GetChildAt(1); - const SEditableTextBox& TooltipBox = static_cast(Child.Get()); - FunctionArgumentDescriptions[i]->Tooltip = TooltipBox.GetText(); - } - - for (int32 i = 0; i < FunctionOutputDescriptions.Num(); i++) - { - TSharedRef Child = FunctionOutputListView->WidgetFromItem(FunctionOutputDescriptions[i])->GetContent()->GetChildren()->GetChildAt(1); - const SEditableTextBox& TooltipBox = static_cast(Child.Get()); - FunctionOutputDescriptions[i]->Tooltip = TooltipBox.GetText(); - } - - FString RawTooltip = FTooltipStringHelper::ConvertTooltipDataToRawTooltip(FunctionDescriptionTooltipBox->GetText(), FunctionArgumentDescriptions, FunctionOutputDescriptions); - - TSharedPtr FunctionPointer = SelectedFunctions[0]; - FunctionPointer->FunctionEntryNode->MetaData.ToolTip = FText::FromString(RawTooltip); -} - -void STooltipTool::RebuildMemberList() -{ - Members.Empty(); - FunctionPointers.Empty(); - FunctionArgumentDescriptions.Empty(); - FunctionOutputDescriptions.Empty(); - - if (!BlueprintComboBox->GetSelectedItem().IsValid()) - { - return; - } - - UBlueprint* Blueprint = Cast(BlueprintComboBox->GetSelectedItem().Get()->GetAsset()); - - // Get variables - for (FBPVariableDescription Member : Blueprint->NewVariables) - { - if ((Member.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) - { - Members.Push(TSharedPtr(new FBPVariableDescription(Member))); - } - } - - // Get functions - for (UEdGraph* FunctionGraph : Blueprint->FunctionGraphs) - { - if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) - { - TWeakObjectPtr FuncEntryPtr; - TWeakObjectPtr FuncResultPtr; - FBlueprintEditorUtils::GetEntryAndResultNodes(FunctionGraph, FuncEntryPtr, FuncResultPtr); - - UK2Node_FunctionEntry* FunctionEntryNode = Cast(FuncEntryPtr.Get()); - UK2Node_FunctionResult* FunctionResultNode = Cast(FuncResultPtr.Get()); - - if (FunctionEntryNode != nullptr && FunctionEntryNode->IsEditable() && FunctionEntryNode->GetFunctionFlags() & FUNC_Public) - { - FunctionPointers.Push(MakeShared(FunctionEntryNode, FunctionResultNode, FunctionEntryNode->GetGraph()->GetFName())); - } - } - } - - MemberListView->RebuildList(); - FunctionListView->RebuildList(); - - if (Members.Num() > 0) - { - MemberListView->SetSelection(Members[0], ESelectInfo::Direct); - } - - if (FunctionPointers.Num() > 0) - { - FunctionListView->SetSelection(FunctionPointers[0], ESelectInfo::Direct); - } -} - -#undef LOCTEXT_NAMESPACE diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReport.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReport.cpp deleted file mode 100644 index 592300b..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReport.cpp +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "UI/LintReport.h" -#include "UI/LintReportAssetError.h" - -#include "LintRule.h" -#include "Widgets/Views/STableRow.h" -#include "Widgets/Views/SListView.h" -#include "Widgets/SBoxPanel.h" -#include "LintRuleSet.h" -#include "UI/LintReportAssetErrorList.h" -#include "UI/LintReportAssetDetails.h" -#include "AssetThumbnail.h" -#include "Containers/Map.h" -#include "LinterSettings.h" -#include "Misc/ScopedSlowTask.h" -#include "Widgets/Layout/SSpacer.h" -#include "Dom/JsonObject.h" -#include "Serialization/JsonWriter.h" -#include "Policies/PrettyJsonPrintPolicy.h" -#include "Serialization/JsonSerializer.h" -#include "Dom/JsonValue.h" -#include "DesktopPlatformModule.h" -#include "IDesktopPlatform.h" -#include "Misc/FileHelper.h" -#include "Widgets/Input/SComboButton.h" -#include "UI/LintReportRuleDetails.h" - -#define LOCTEXT_NAMESPACE "Linter" - -void SLintReport::Construct(const FArguments& Args) -{ - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - ChildSlot - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .VAlign(VAlign_Fill) - .AutoHeight() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .HAlign(HAlign_Left) - .AutoWidth() - .Padding(PaddingAmount) - [ - SNew(SButton) - .Text(LOCTEXT("Rescan", "Rescan")) - .OnClicked_Lambda([this]() -> FReply { Rebuild(LastUsedRuleSet); return FReply::Handled(); }) - ] - + SHorizontalBox::Slot() - .HAlign(HAlign_Left) - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(PaddingAmount) - [ - SAssignNew(ResultsTextBlockPtr, STextBlock) - ] - + SHorizontalBox::Slot() - .HAlign(HAlign_Fill) - .VAlign(VAlign_Center) - .FillWidth(1.0f) - .Padding(PaddingAmount) - [ - SNew(SSpacer) - ] - + SHorizontalBox::Slot() - .HAlign(HAlign_Right) - .AutoWidth() - .Padding(PaddingAmount) - [ - SNew(SButton) - .Text(LOCTEXT("ExportToJSON", "Export To JSON")) - .OnClicked_Lambda([this]() -> FReply - { - IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); - - const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr); - - const FText Title = LOCTEXT("ExportToJsonTitle", "Export Lint Report as JSON"); - const FString FileTypes = TEXT("Json (*.json)|*.json"); - - FDateTime Now = FDateTime::Now(); - FString Output = TEXT("lint-report-") + FDateTime::Now().ToString() + TEXT(".json"); - - FString DefaultPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); - DefaultPath = FPaths::ConvertRelativePathToFull(DefaultPath); - IFileManager::Get().MakeDirectory(*DefaultPath, true); - - TArray OutFilenames; - DesktopPlatform->SaveFileDialog( - ParentWindowWindowHandle, - Title.ToString(), - DefaultPath, - Output, - FileTypes, - EFileDialogFlags::None, - OutFilenames - ); - - if (OutFilenames.Num() > 0) - { - FString WritePath = FPaths::ConvertRelativePathToFull(OutFilenames[0]); - FFileHelper::SaveStringToFile(JsonReport, *WritePath); - FPlatformProcess::LaunchURL(*WritePath, TEXT(""), nullptr); - } - - return FReply::Handled(); - }) - ] - + SHorizontalBox::Slot() - .HAlign(HAlign_Right) - .AutoWidth() - .Padding(PaddingAmount) - [ - SNew(SButton) - .Text(LOCTEXT("ExportToHTML", "Export To HTML")) - .OnClicked_Lambda([this]() -> FReply - { - IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); - - const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr); - - const FText Title = LOCTEXT("ExportToHTMLTitle", "Export Lint Report as HTML"); - const FString FileTypes = TEXT("HTML (*.html)|*.html"); - - FDateTime Now = FDateTime::Now(); - FString Output = TEXT("lint-report-") + FDateTime::Now().ToString() + TEXT(".html"); - - FString DefaultPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); - DefaultPath = FPaths::ConvertRelativePathToFull(DefaultPath); - IFileManager::Get().MakeDirectory(*DefaultPath, true); - - TArray OutFilenames; - DesktopPlatform->SaveFileDialog( - ParentWindowWindowHandle, - Title.ToString(), - DefaultPath, - Output, - FileTypes, - EFileDialogFlags::None, - OutFilenames - ); - - if (OutFilenames.Num() > 0) - { - FString WritePath = FPaths::ConvertRelativePathToFull(OutFilenames[0]); - FFileHelper::SaveStringToFile(HTMLReport, *WritePath); - FPlatformProcess::LaunchURL(*WritePath, TEXT(""), nullptr); - } - - return FReply::Handled(); - }) - ] - ] - + SVerticalBox::Slot() - .VAlign(VAlign_Fill) - .FillHeight(1.0f) - .Padding(PaddingAmount) - [ - SAssignNew(AssetDetailsScrollBoxPtr, SScrollBox) - .ScrollBarAlwaysVisible(true) - ] - + SVerticalBox::Slot() - .VAlign(VAlign_Fill) - .FillHeight(1.0f) - .Padding(PaddingAmount) - [ - SAssignNew(RuleDetailsScrollBoxPtr, SScrollBox) - .ScrollBarAlwaysVisible(true) - .Visibility(EVisibility::Collapsed) - ] - // Bottom panel - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Top) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(FMargin(4.0f, 0.0f, 4.0f, 2.0f)) - //.Visibility_Lambda([&]() { return AssetErrorLists.Num() > 0 ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; }) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(FMargin(2.0f, 0.0f, 2.0f, 2.0f)) - [ - - SNew(SHorizontalBox) - // View mode combo button - +SHorizontalBox::Slot() - .FillWidth(1.f) - .VAlign(VAlign_Center) - .HAlign(HAlign_Right) - [ - SAssignNew( ViewOptionsComboButton, SComboButton ) - .ContentPadding(0) - .ForegroundColor_Lambda([&]() { return ViewOptionsComboButton->IsHovered() ? FEditorStyle::GetSlateColor("InvertedForeground") : FEditorStyle::GetSlateColor("DefaultForeground"); }) - .ButtonStyle( FEditorStyle::Get(), "ToggleButton" ) // Use the tool bar item style for this button - .OnGetMenuContent( this, &SLintReport::GetViewButtonContent ) - .ButtonContent() - [ - SNew(SHorizontalBox) - +SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - [ - SNew(SImage).Image( FEditorStyle::GetBrush("GenericViewButton") ) - ] - - +SHorizontalBox::Slot() - .AutoWidth() - .Padding(2, 0, 0, 0) - .VAlign(VAlign_Center) - [ - SNew(STextBlock).Text( LOCTEXT("ViewButton", "View Options") ) - ] - ] - ] - ] - ] - ] - ]; -} - -void SLintReport::Rebuild(const ULintRuleSet* SelectedLintRuleSet) -{ - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - NumErrors = 0; - NumWarnings = 0; - bHasRanReport = false; - - if (SelectedLintRuleSet == nullptr) - { - SelectedLintRuleSet = GetDefault()->DefaultLintRuleSet.LoadSynchronous(); - } - - check(SelectedLintRuleSet != nullptr); - LastUsedRuleSet = SelectedLintRuleSet; - - AssetDetailsScrollBoxPtr->ClearChildren(); - RuleDetailsScrollBoxPtr->ClearChildren(); - RuleViolations.Reset(); - - FScopedSlowTask SlowTask(0, LOCTEXT("LintingInProgress", "Linting Assets...")); - SlowTask.MakeDialog(false); - - FLinterModule& LinterModule = FModuleManager::LoadModuleChecked(TEXT("Linter")); - TArray LintPaths = LinterModule.GetDesiredLintPaths(); - - RuleViolations = SelectedLintRuleSet->LintPathShared(LintPaths, &SlowTask); - - for (TSharedPtr Violation : RuleViolations) - { - if (Violation->ViolatedRule->GetDefaultObject()->RuleSeverity <= ELintRuleSeverity::Error) - { - NumErrors++; - } - else - { - NumWarnings++; - } - } - - TArray UniqueViolators = FLintRuleViolation::AllRuleViolationViolators(RuleViolations); - TSharedPtr ThumbnailPool = MakeShareable(new FAssetThumbnailPool(UniqueViolators.Num())); - - TSharedPtr RootJsonObject = MakeShareable(new FJsonObject); - TArray> ViolatorJsonObjects; - - for (UObject* Violator : UniqueViolators) - { - // We might as well build JSON data here as we're iterating through all our rule violations anyway - TSharedPtr AssetJsonObject = MakeShareable(new FJsonObject); - TArray> UniqueViolatorViolations = FLintRuleViolation::AllRuleViolationsWithViolatorShared(RuleViolations, Violator); - - FAssetData AssetData; - if (UniqueViolatorViolations.Num() > 0) - { - AssetData = UniqueViolatorViolations[0]->ViolatorAssetData; - AssetJsonObject->SetStringField(TEXT("ViolatorAssetName"), AssetData.AssetName.ToString()); - AssetJsonObject->SetStringField(TEXT("ViolatorAssetPath"), AssetData.ObjectPath.ToString()); - AssetJsonObject->SetStringField(TEXT("ViolatorFullName"), AssetData.GetFullName()); - //@TODO: Thumbnail export? - - TArray> RuleViolationJsonObjects; - - for (TSharedPtr Violation : UniqueViolatorViolations) - { - ULintRule* LintRule = Violation->ViolatedRule->GetDefaultObject(); - check(LintRule != nullptr); - - TSharedPtr RuleJsonObject = MakeShareable(new FJsonObject); - RuleJsonObject->SetStringField(TEXT("RuleGroup"), LintRule->RuleGroup.ToString()); - RuleJsonObject->SetStringField(TEXT("RuleTitle"), LintRule->RuleTitle.ToString()); - RuleJsonObject->SetStringField(TEXT("RuleDesc"), LintRule->RuleDescription.ToString()); - RuleJsonObject->SetStringField(TEXT("RuleURL"), LintRule->RuleURL); - RuleJsonObject->SetNumberField(TEXT("RuleSeverity"), (int32)LintRule->RuleSeverity); - RuleJsonObject->SetStringField(TEXT("RuleRecommendedAction"), Violation->RecommendedAction.ToString()); - RuleViolationJsonObjects.Push(MakeShareable(new FJsonValueObject(RuleJsonObject))); - } - - AssetJsonObject->SetArrayField(TEXT("Violations"), RuleViolationJsonObjects); - } - - ViolatorJsonObjects.Add(MakeShareable(new FJsonValueObject(AssetJsonObject))); - - AssetDetailsScrollBoxPtr.Get()->AddSlot() - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .Padding(PaddingAmount) - [ - SNew(SLintReportAssetDetails) - .AssetData(AssetData) - .RuleViolations(UniqueViolatorViolations) - .ThumbnailPool(ThumbnailPool) - ]; - } - - TMultiMap> ViolationsMappedByRule = FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRuleShared(RuleViolations); - TSharedPtr RuleThumbnailPool = MakeShareable(new FAssetThumbnailPool(ViolationsMappedByRule.Num())); // Incase we ever want to render 'rule thumbnails' in the future - - TArray UniqueRules; - ViolationsMappedByRule.GetKeys(UniqueRules); - - for (const ULintRule* BrokenRule : UniqueRules) - { - TArray> ViolatorsOfBrokenRule; - ViolationsMappedByRule.MultiFind(BrokenRule, ViolatorsOfBrokenRule); - - if (ViolatorsOfBrokenRule.Num() > 0) - { - RuleDetailsScrollBoxPtr.Get()->AddSlot() - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .Padding(PaddingAmount) - [ - SNew(SLintReportRuleDetails) - .RuleViolations(ViolatorsOfBrokenRule) - .ThumbnailPool(RuleThumbnailPool) - ]; - } - } - - // Save off our JSON to a string - RootJsonObject->SetArrayField(TEXT("Violators"), ViolatorJsonObjects); - JsonReport.Empty(); - TSharedRef>> Writer = TJsonWriterFactory>::Create(&JsonReport); - FJsonSerializer::Serialize(RootJsonObject.ToSharedRef(), Writer); - - // Update Summary Text Block - int32 NumAssets = UniqueViolators.Num(); - FText ResultsSummary = FText::FormatNamed(LOCTEXT("ErrorWarningDisplay", "{NumAssets} {NumAssets}|plural(one=Asset,other=Assets), {NumErrors} {NumErrors}|plural(one=Error,other=Errors), {NumWarnings} {NumWarnings}|plural(one=Warning,other=Warnings)"), TEXT("NumAssets"), NumAssets, TEXT("NumErrors"), NumErrors, TEXT("NumWarnings"), NumWarnings); - ResultsTextBlockPtr->SetText(ResultsSummary); - - // Prepare the HTML Export - FString TemplatePath = FPaths::Combine(*IPluginManager::Get().FindPlugin(TEXT("Linter"))->GetBaseDir(), TEXT("Resources"), TEXT("LintReportTemplate.html")); - - if (FFileHelper::LoadFileToString(HTMLReport, *TemplatePath)) - { - HTMLReport.ReplaceInline(TEXT("{% TITLE %}"), *FPaths::GetBaseFilename(FPaths::GetProjectFilePath())); - HTMLReport.ReplaceInline(TEXT("{% RESULTS %}"), *ResultsSummary.ToString()); - HTMLReport.ReplaceInline(TEXT("{% LINT_REPORT %}"), *JsonReport); - } - - bHasRanReport = true; -} - -TSharedRef SLintReport::GetViewButtonContent() -{ - FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr, TSharedPtr(), /*bCloseSelfOnly=*/ true); - - MenuBuilder.BeginSection("AssetViewType", LOCTEXT("ViewTypeHeading", "View Type")); - { - MenuBuilder.AddMenuEntry( - LOCTEXT("RuleFirstOption", "Assets by Guideline"), - LOCTEXT("RuleFirstOptionTooltip", "View failing assets as a categorized list of guidelines."), - FSlateIcon(), - FUIAction( - FExecuteAction::CreateLambda([&]() - { - if (AssetDetailsScrollBoxPtr.IsValid()) - { - AssetDetailsScrollBoxPtr->SetVisibility(EVisibility::SelfHitTestInvisible); - } - if (RuleDetailsScrollBoxPtr.IsValid()) - { - RuleDetailsScrollBoxPtr->SetVisibility(EVisibility::Collapsed); - } - }), - FCanExecuteAction(), - FIsActionChecked::CreateLambda([&]() { return AssetDetailsScrollBoxPtr.IsValid() && AssetDetailsScrollBoxPtr->GetVisibility().IsVisible(); }) - ), - NAME_None, - EUserInterfaceActionType::RadioButton - ); - - MenuBuilder.AddMenuEntry( - LOCTEXT("AssetsFirstOption", "Guidelines by Asset"), - LOCTEXT("AssetsFirstOptionTooltip", "View the failing assets one at a time with all their respective errors."), - FSlateIcon(), - FUIAction( - FExecuteAction::CreateLambda([&]() - { - if (AssetDetailsScrollBoxPtr.IsValid()) - { - AssetDetailsScrollBoxPtr->SetVisibility(EVisibility::Collapsed); - } - if (RuleDetailsScrollBoxPtr.IsValid()) - { - RuleDetailsScrollBoxPtr->SetVisibility(EVisibility::SelfHitTestInvisible); - } - }), - FCanExecuteAction(), - FIsActionChecked::CreateLambda([&]() { return RuleDetailsScrollBoxPtr.IsValid() && RuleDetailsScrollBoxPtr->GetVisibility().IsVisible(); }) - ), - NAME_None, - EUserInterfaceActionType::RadioButton - ); - } - - MenuBuilder.EndSection(); - - return MenuBuilder.MakeWidget(); -} - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetDetails.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetDetails.cpp deleted file mode 100644 index c2e9b28..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetDetails.cpp +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "UI/LintReportAssetDetails.h" -#include "LinterStyle.h" -#include "Widgets/Layout/SBorder.h" -#include "EditorStyleSet.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "ContentBrowserModule.h" -#include "IContentBrowserSingleton.h" -#include "AssetRegistryModule.h" -#include "Widgets/Input/SHyperlink.h" -#include "Widgets/Layout/SSpacer.h" -#include "IAssetTools.h" -#include "AssetToolsModule.h" -#include "Misc/MessageDialog.h" -#include "Internationalization/Internationalization.h" -#include "Widgets/Text/STextBlock.h" -#include "Framework/Views/ITypedTableView.h" -#include "UI/LintReportAssetError.h" -#include "LintRule.h" -#include "AssetThumbnail.h" - - -#define LOCTEXT_NAMESPACE "LintReport" - -void SLintReportAssetDetails::Construct(const FArguments& Args) -{ - AssetData = Args._AssetData; - RuleViolations = Args._RuleViolations; - ThumbnailPool = Args._ThumbnailPool; - - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - FText AssetName = FText::FromString(AssetData.Get().AssetName.ToString()); - FText AssetPath = FText::FromString(AssetData.Get().GetFullName()); - - const TSharedPtr AssetThumbnail = MakeShareable(new FAssetThumbnail(AssetData.Get().GetAsset(), 96, 96, ThumbnailPool.Get())); - - int32 NumErrors = 0; - int32 NumWarnings = 0; - - for (TSharedPtr RuleViolation : RuleViolations.Get()) - { - switch (RuleViolation->ViolatedRule.Get()->GetDefaultObject()->RuleSeverity) - { - case ELintRuleSeverity::Error: - NumErrors++; - break; - case ELintRuleSeverity::Warning: - NumWarnings++; - break; - case ELintRuleSeverity::Info: - break; - //case ELintRuleSeverity::Ignore: - default: - break; - } - } - - ChildSlot - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(PaddingAmount) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(PaddingAmount) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SExpandableArea) - .InitiallyCollapsed(false) - .HeaderContent() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(STextBlock) - .Text(AssetName) - .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") - ] - ] - .BodyContent() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Top) - .Padding(PaddingAmount) - [ - SNew(SBox) - .WidthOverride(96.0f) - .HeightOverride(96.0f) - [ - AssetThumbnail->MakeThumbnailWidget() - ] - ] - + SHorizontalBox::Slot() - .Padding(PaddingAmount) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .HAlign(HAlign_Left) - .Padding(PaddingAmount) - [ - SNew(SHyperlink) - .Text(AssetPath) - .OnNavigate_Lambda([&]() - { - FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); - TArray AssetDatas; - AssetDatas.Push(AssetData.Get()); - ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); - }) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(STextBlock) - .Visibility((RuleViolations.Get().Num() > 0) ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed) - .Text(FText::FormatNamed(LOCTEXT("ErrorWarningDisplay", "{NumErrors} {NumErrors}|plural(one=Error,other=Errors), {NumWarnings} {NumWarnings}|plural(one=Warning,other=Warnings)"), TEXT("NumErrors"), NumErrors, TEXT("NumWarnings"), NumWarnings)) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SLintReportAssetErrorList) - .RuleViolations(RuleViolations) - ] - ] - ] - ] - ] - ] - ]; -} diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetError.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetError.cpp deleted file mode 100644 index 1df22b6..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetError.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "UI/LintReportAssetError.h" -#include "Widgets/Layout/SBorder.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "Widgets/Input/SHyperlink.h" -#include "LintRule.h" -#include "Widgets/Views/SListView.h" -#include "Widgets/Views/STableRow.h" -#include "Widgets/Layout/SBox.h" - -#define LOCTEXT_NAMESPACE "LintReport" - - -void SLintReportAssetError::Construct(const FArguments& Args) -{ - RuleViolation = Args._RuleViolation; - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - ULintRule* LintRule = RuleViolation.Get()->ViolatedRule.Get()->GetDefaultObject(); - check(LintRule != nullptr); - - const FSlateBrush* RuleIcon = nullptr; - bool bHasURL = !LintRule->RuleURL.IsEmpty(); - - switch (LintRule->RuleSeverity) - { - case ELintRuleSeverity::Error: - RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Error"); - break; - case ELintRuleSeverity::Warning: - RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Warning"); - break; - case ELintRuleSeverity::Info: - RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Info"); - break; - //case ELintRuleSeverity::Ignore: - default: - break; - } - - ChildSlot - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Center) - .Padding(PaddingAmount) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .Padding(PaddingAmount) - .AutoWidth() - .VAlign(VAlign_Center) - .HAlign(HAlign_Left) - [ - SNew(SBox) - .WidthOverride(14.0f) - .HeightOverride(14.0f) - [ - SNew(SImage) - .Image(RuleIcon) - ] - ] - + SHorizontalBox::Slot() - .Padding(PaddingAmount) - .AutoWidth() - .HAlign(HAlign_Left) - .VAlign(VAlign_Center) - [ - SNew(STextBlock) - .Text(LintRule->RuleTitle) - .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") - ] - + SHorizontalBox::Slot() - .AutoWidth() - .HAlign(HAlign_Left) - .VAlign(VAlign_Center) - [ - SNew(SBox) - .WidthOverride(16.0f) - .HeightOverride(16.0f) - [ - SNew(SImage) - .Cursor(EMouseCursor::Hand) - .Visibility(bHasURL ? EVisibility::Visible : EVisibility::Collapsed) - .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { FPlatformProcess::LaunchURL(*RuleViolation.Get()->ViolatedRule.Get()->GetDefaultObject()->RuleURL, NULL, NULL); return FReply::Handled(); }) - .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Top) - .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) - [ - SNew(STextBlock) - .AutoWrapText(true) - .Text(LintRule->RuleDescription) - ] - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Top) - .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) - [ - SNew(STextBlock) - .AutoWrapText(true) - .Text(RuleViolation.Get()->RecommendedAction) - ] - ]; -} - diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetErrorList.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetErrorList.cpp deleted file mode 100644 index 1d1bf8c..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReportAssetErrorList.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "UI/LintReportAssetErrorList.h" -#include "LinterStyle.h" -#include "Widgets/Layout/SBorder.h" -#include "EditorStyleSet.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "ContentBrowserModule.h" -#include "IContentBrowserSingleton.h" -#include "AssetRegistryModule.h" -#include "Widgets/Input/SHyperlink.h" -#include "Widgets/Layout/SSpacer.h" -#include "IAssetTools.h" -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "AssetToolsModule.h" -#include "Misc/MessageDialog.h" -#include "Internationalization/Internationalization.h" -#include "Widgets/Text/STextBlock.h" -#include "Framework/Views/ITypedTableView.h" -#include "UI/LintReportAssetError.h" -#include "LintRule.h" - -#define LOCTEXT_NAMESPACE "LintReport" - -void SLintReportAssetErrorList::Construct(const FArguments& Args) -{ - RuleViolations = Args._RuleViolations; - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - ChildSlot - [ - SNew(SListView>) - .SelectionMode(ESelectionMode::None) - .ListItemsSource(&RuleViolations.Get()) - .OnGenerateRow_Lambda([&](TSharedPtr InItem, const TSharedRef& OwnerTable) - { - return SNew(STableRow>, OwnerTable) - [ - SNew(SLintReportAssetError) - .RuleViolation(InItem) - ]; - }) - ]; -} diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleDetails.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleDetails.cpp deleted file mode 100644 index f1d1e4a..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleDetails.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "UI/LintReportRuleDetails.h" -#include "LinterStyle.h" -#include "Widgets/Layout/SBorder.h" -#include "EditorStyleSet.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "ContentBrowserModule.h" -#include "IContentBrowserSingleton.h" -#include "AssetRegistryModule.h" -#include "Widgets/Input/SHyperlink.h" -#include "Widgets/Layout/SSpacer.h" -#include "IAssetTools.h" -#include "AssetToolsModule.h" -#include "Misc/MessageDialog.h" -#include "Internationalization/Internationalization.h" -#include "Widgets/Text/STextBlock.h" -#include "Framework/Views/ITypedTableView.h" -#include "UI/LintReportRuleErrorList.h" -#include "LintRule.h" -#include "AssetThumbnail.h" - - - -#define LOCTEXT_NAMESPACE "LintReport" - -void SLintReportRuleDetails::Construct(const FArguments& Args) -{ - RuleViolations = Args._RuleViolations; - ThumbnailPool = Args._ThumbnailPool; - - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - check(RuleViolations.Get().Num() > 0 ); - ULintRule* BrokenRule = (RuleViolations.Get())[0]->ViolatedRule.GetDefaultObject(); - check(BrokenRule != nullptr); - - FText RuleName = BrokenRule->RuleTitle; - FText RuleDesc = BrokenRule->RuleDescription; - - RuleURL = BrokenRule->RuleURL; - - const FSlateBrush* RuleIcon = nullptr; - switch (BrokenRule->RuleSeverity) - { - case ELintRuleSeverity::Error: - RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Error"); - break; - case ELintRuleSeverity::Warning: - RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Warning"); - break; - case ELintRuleSeverity::Info: - RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Info"); - break; - //case ELintRuleSeverity::Ignore: - default: - break; - } - - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); - IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); - - RuleAssetData = AssetRegistry.GetAssetByObjectPath(FName(*BrokenRule->GetPathName()), true); - FText RuleAssetPath; - if (RuleAssetData.IsValid()) - { - RuleAssetPath = FText::FromName(RuleAssetData.PackagePath); - } - - - - ChildSlot - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(PaddingAmount) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(PaddingAmount) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SExpandableArea) - .InitiallyCollapsed(false) - .HeaderContent() - [ - SNew(SHorizontalBox) - // Rule Thumbnail - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Top) - .Padding(PaddingAmount) - [ - SAssignNew(ThumbnailBox, SBox) - .WidthOverride(96.0f) - .HeightOverride(96.0f) - .Visibility(RuleAssetData.IsValid() ? EVisibility::HitTestInvisible : EVisibility::Collapsed) - ] - // Rule Icon - + SHorizontalBox::Slot() - .Padding(PaddingAmount) - .AutoWidth() - .VAlign(VAlign_Center) - .HAlign(HAlign_Left) - [ - SNew(SBox) - .WidthOverride(14.0f) - .HeightOverride(14.0f) - [ - SNew(SImage) - .Image(RuleIcon) - ] - ] - // Rule Name - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(STextBlock) - .Text(RuleName) - .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") - ] - // Link to Rule URL - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(8.0f, 0.0) - [ - SNew(SImage) - .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) - .Cursor(EMouseCursor::Hand) - .Visibility(RuleURL.IsEmpty() ? EVisibility::Collapsed : EVisibility::Visible) - .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { FPlatformProcess::LaunchURL(*RuleURL, NULL, NULL); return FReply::Handled(); }) - ] - // Link to Rule Definition Asset - + SHorizontalBox::Slot() - .AutoWidth() - .Padding(8.0f, 0.0) - [ - SNew(SImage) - .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) - .Cursor(EMouseCursor::Hand) - .Visibility(EVisibility::Collapsed) - .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) - { - FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); - ContentBrowserModule.Get().SyncBrowserToAssets(TArray({ RuleAssetData })); - return FReply::Handled(); - }) - ] - // Asset Count - + SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .AutoWidth() - .Padding(8.0f, 0.0) - [ - SNew(SHyperlink) - .Text(FText::FormatNamed(LOCTEXT("AssetCountDisplay", "{NumAssets} {NumAssets}|plural(one=Asset,other=Assets)"), TEXT("NumAssets"), RuleViolations.Get().Num())) - .OnNavigate_Lambda([&]() - { - FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); - TArray AssetDatas; - - for (const TSharedPtr& RuleViolation : RuleViolations.Get()) - { - AssetDatas.Push(RuleViolation->ViolatorAssetData); - } - - ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); - }) - ] - ] - .BodyContent() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(PaddingAmount) - .HAlign(HAlign_Left) - .AutoHeight() - [ - SNew(STextBlock) - .Text(RuleDesc) - .AutoWrapText(true) - .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") - ] - + SVerticalBox::Slot() - .Padding(PaddingAmount) - .HAlign(HAlign_Left) - .AutoHeight() - [ - SNew(SLintReportRuleErrorList) - .RuleViolations(RuleViolations) - ] - ] - ] - ] - ] - ]; - - if (RuleAssetData.IsValid()) - { - const TSharedPtr RuleThumbnail = MakeShareable(new FAssetThumbnail(RuleAssetData, 96, 96, ThumbnailPool.Get())); - ThumbnailBox->SetContent(RuleThumbnail->MakeThumbnailWidget()); - } -} diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleError.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleError.cpp deleted file mode 100644 index b8d22a6..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleError.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#include "UI/LintReportRuleError.h" -#include "Widgets/Layout/SBorder.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "Widgets/Input/SHyperlink.h" -#include "LintRule.h" -#include "Widgets/Views/SListView.h" -#include "Widgets/Views/STableRow.h" -#include "Widgets/Layout/SBox.h" - -#define LOCTEXT_NAMESPACE "LintReport" - - -void SLintReportRuleError::Construct(const FArguments& Args) -{ - RuleViolation = Args._RuleViolation; - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - ChildSlot - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Center) - .Padding(PaddingAmount) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .Padding(PaddingAmount) - .AutoWidth() - .HAlign(HAlign_Left) - .VAlign(VAlign_Center) - [ - SNew(SHyperlink) - .Text(FText::FromName(RuleViolation.Get()->ViolatorAssetData.PackageName)) - .OnNavigate_Lambda([&]() - { - FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); - TArray AssetDatas; - AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(RuleViolation.Get()->ViolatorAssetData.ObjectPath)); - ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); - }) - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Top) - .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) - [ - SNew(STextBlock) - .AutoWrapText(true) - .Text(RuleViolation.Get()->RecommendedAction) - .Visibility(RuleViolation.Get()->RecommendedAction.IsEmpty() ? EVisibility::Collapsed : EVisibility::SelfHitTestInvisible) - ] - ]; -} - diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleErrorList.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleErrorList.cpp deleted file mode 100644 index 4f411a5..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintReportRuleErrorList.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#include "UI/LintReportruleErrorList.h" -#include "LinterStyle.h" -#include "Widgets/Layout/SBorder.h" -#include "EditorStyleSet.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SExpandableArea.h" -#include "ContentBrowserModule.h" -#include "IContentBrowserSingleton.h" -#include "AssetRegistryModule.h" -#include "Widgets/Input/SHyperlink.h" -#include "Widgets/Layout/SSpacer.h" -#include "IAssetTools.h" -#include "AssetToolsModule.h" -#include "Misc/MessageDialog.h" -#include "Internationalization/Internationalization.h" -#include "Widgets/Text/STextBlock.h" -#include "Framework/Views/ITypedTableView.h" -#include "UI/LintReportAssetError.h" -#include "LintRule.h" - -#define LOCTEXT_NAMESPACE "LintReport" - -void SLintReportRuleErrorList::Construct(const FArguments& Args) -{ - RuleViolations = Args._RuleViolations; - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - ChildSlot - [ - SNew(SListView>) - .SelectionMode(ESelectionMode::None) - .ListItemsSource(&RuleViolations.Get()) - .OnGenerateRow_Lambda([&](TSharedPtr InItem, const TSharedRef& OwnerTable) - { - return SNew(STableRow>, OwnerTable) - [ - SNew(SLintReportRuleError) - .RuleViolation(InItem) - ]; - }) - ]; -} diff --git a/Plugins/Linter/Source/Linter/Private/UI/LintWizard.cpp b/Plugins/Linter/Source/Linter/Private/UI/LintWizard.cpp deleted file mode 100644 index 65e9cbd..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/LintWizard.cpp +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2015-2017 by Gamemakin LLC -#include "UI/LintWizard.h" - -#include "CoreGlobals.h" -#include "Delegates/Delegate.h" -#include "AssetRegistryModule.h" -#include "IAssetRegistry.h" -#include "AssetData.h" -#include "SlateOptMacros.h" -#include "Widgets/Layout/SSeparator.h" -#include "Widgets/Views/SListView.h" -#include "Widgets/Text/SRichTextBlock.h" -#include "Widgets/Notifications/SNotificationList.h" -#include "Widgets/Layout/SScrollBox.h" -#include "IUATHelperModule.h" -#include "Misc/FeedbackContext.h" -#include "Framework/Notifications/NotificationManager.h" -#include "AssetThumbnail.h" -#include "FileHelpers.h" -#include "Logging/MessageLog.h" -#include "Logging/TokenizedMessage.h" - -#include "LinterStyle.h" -#include "LintRuleSet.h" -#include "LinterSettings.h" -#include "UI/SAssetLinkWidget.h" - - - -BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION -void SLintWizard::Construct(const FArguments& InArgs) -{ - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - - RuleSets = TArray>(); - - // Try to load the default rule set - ULintRuleSet* DefaultRuleSet = GetDefault()->DefaultLintRuleSet.LoadSynchronous(); - - // Even though this happens on module startup, we try force loading all rule sets again in case any new unloaded rule sets have been added - // or for some reason our existing rule sets were unloaded from memory - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); - IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); - - TArray FoundRuleSets; - AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); - - // Attempt to get all RuleSets in memory so that linting tools are better aware of them - for (const FAssetData& RuleSetData : FoundRuleSets) - { - ULintRuleSet* LoadedRuleSet = Cast(RuleSetData.GetAsset()); - if (LoadedRuleSet != nullptr) - { - FAssetData* newData = new FAssetData; - *newData = RuleSetData; - RuleSets.Push(MakeShareable(newData)); - if (LoadedRuleSet == DefaultRuleSet) - { - SelectedRuleSet = RuleSets.Last(); - } - } - } - - if (!SelectedRuleSet.IsValid() && RuleSets.Num() > 0) - { - SelectedRuleSet = RuleSets[0]; - } - - ChildSlot - [ - SNew(SBorder) - .Padding(18) - .BorderImage(FEditorStyle::GetBrush("Docking.Tab.ContentAreaBrush")) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - [ - SAssignNew(MainWizard, SWizard) - .ShowPageList(false) - .ShowCancelButton(false) - .ButtonStyle(FEditorStyle::Get(), "FlatButton.Default") - .CancelButtonStyle(FEditorStyle::Get(), "FlatButton.Default") - .FinishButtonStyle(FEditorStyle::Get(), "FlatButton.Success") - .ButtonTextStyle(FEditorStyle::Get(), "LargeText") - .ForegroundColor(FEditorStyle::Get().GetSlateColor("WhiteBrush")) - .CanFinish(true) - .FinishButtonText(LOCTEXT("FinishButtonText", "Close")) - .OnFinished_Lambda([&]() - { - FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab"))->RequestCloseTab(); - }) - + SWizard::Page() - .CanShow_Lambda([&]() { return RuleSets.Num() > 0; }) - [ - SNew(SVerticalBox) - // Title - +SVerticalBox::Slot() - .AutoHeight() - .Padding(0) - [ - SNew(STextBlock) - .TextStyle( FEditorStyle::Get(), "NewClassDialog.PageTitle" ) - .Text(LOCTEXT("LinterSelectionTitle", "Linter Rule Set Selection")) - ] - // Title spacer - +SVerticalBox::Slot() - .AutoHeight() - .Padding(0, 2, 0, 8) - [ - SNew(SSeparator) - ] - // Linter Selection - +SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SAssignNew(RuleSetSelectionComboBox, SComboBox>) - .OptionsSource(&RuleSets) - .InitiallySelectedItem(SelectedRuleSet) - .OnGenerateWidget_Lambda([&](TSharedPtr LintRuleSet) - { - ULintRuleSet* RuleSet = Cast(LintRuleSet->GetAsset()); - if (RuleSet != nullptr) - { - return SNew(STextBlock).Text(RuleSet->RuleSetDescription.IsEmpty() ? FText::FromString(RuleSet->GetPathName()) : RuleSet->RuleSetDescription); - } - return SNew(STextBlock).Text(FText::FromString(TEXT("This Lint Rule Set Failed To Load? Uhhhh...."))); - }) - .OnSelectionChanged_Lambda([&](TSharedPtr Item, ESelectInfo::Type SelectInfo) { SelectedRuleSet = Item; RuleSetSelectionComboBox->RefreshOptions(); }) - .ContentPadding(4.0f) - [ - SNew(STextBlock) - .Text_Lambda([&]() { return SelectedRuleSet->GetAsset() != nullptr ? Cast(SelectedRuleSet->GetAsset())->RuleSetDescription : FText::GetEmpty(); }) - ] - ] - ] - // Lint Report - + SWizard::Page() - .OnEnter(this, &SLintWizard::OnLintReportEntered) - .CanShow_Lambda([&]() { return RuleSets.Num() >= 1 && SelectedRuleSet.IsValid(); }) - [ - SNew(SVerticalBox) - // Title - + SVerticalBox::Slot() - .AutoHeight() - .Padding(0) - [ - SNew(STextBlock) - .TextStyle( FEditorStyle::Get(), "NewClassDialog.PageTitle" ) - .Text(LOCTEXT("LinterReportTitle", "Lint Report")) - ] - // Marketplace No Errors Required Text - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(STextBlock) - .Text(LOCTEXT("MarketplaceNoErrorsRequired", "The Epic Marketplace requires you to have zero linting errors before submission and approval.")) - .Visibility_Lambda([&]() { return (LintReport.IsValid() && LintReport->bHasRanReport && LintReport->NumErrors > 0 && LintReport->LastUsedRuleSet != nullptr && LintReport->LastUsedRuleSet->bShowMarketplacePublishingInfoInLintWizard) ? EVisibility::HitTestInvisible : EVisibility::Collapsed; }) - ] - // Title spacer - +SVerticalBox::Slot() - .AutoHeight() - .Padding(0, 2, 0, 8) - [ - SNew(SSeparator) - ] - // Linter Report - +SVerticalBox::Slot() - .FillHeight(1.0f) - .VAlign(VAlign_Fill) - .HAlign(HAlign_Fill) - .Padding(PaddingAmount) - [ - SAssignNew(LintReport, SLintReport) - ] - ] - // Marketplace Info Page - + SWizard::Page() - .OnEnter(this, &SLintWizard::OnMarketplaceRecommendationsEntered) - .CanShow_Lambda([&]() { return LintReport.IsValid() && LintReport->bHasRanReport && LintReport->NumErrors <= 0 && LintReport->LastUsedRuleSet != nullptr && LintReport->LastUsedRuleSet->bShowMarketplacePublishingInfoInLintWizard; }) - [ - SNew(SVerticalBox) - // Title - +SVerticalBox::Slot() - .AutoHeight() - .Padding(0) - [ - SNew(STextBlock) - .TextStyle( FEditorStyle::Get(), "NewClassDialog.PageTitle" ) - .Text(LOCTEXT("MarketplaceInfoTitle", "Marketplace Recommendations")) - ] - // Title spacer - +SVerticalBox::Slot() - .AutoHeight() - .Padding(0, 2, 0, 8) - [ - SNew(SSeparator) - ] - + SVerticalBox::Slot() - .FillHeight(1.0f) - [ - SNew(SScrollBox) - + SScrollBox::Slot() - [ - SNew(SVerticalBox) - // Disclaimer - + SVerticalBox::Slot() - .VAlign(VAlign_Top) - .AutoHeight() - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(PaddingAmount) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(PaddingAmount) - [ - SNew(SHorizontalBox) - // Info image - + SHorizontalBox::Slot() - .AutoWidth() - .VAlign(VAlign_Center) - .Padding(28.0f, 8.0f) - [ - SNew(SImage).Image(FLinterStyle::Get()->GetBrush("Linter.Report.Info")) - ] - // Disclaimer text - +SHorizontalBox::Slot() - .VAlign(VAlign_Center) - .FillWidth(1.0f) - [ - SNew(SRichTextBlock) - .Text(LOCTEXT("SuccessDisclaimer", "This product has successfully passed the Marketplace Guidlines Linter scan. Please note, however, that this does not guarantee this product's acceptance onto the Marketplace. We also recommend that you take the following actions listed below.")) - .AutoWrapText(true) - .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") - ] - ] - ] - ] - // Fix Up Redirectors step widget - + SVerticalBox::Slot() - .AutoHeight() - .VAlign(VAlign_Fill) - .HAlign(HAlign_Fill) - .Padding(PaddingAmount) - [ - SNew(SStepWidget) - .StepName(LOCTEXT("FixUpRedirectsStepName", "Fix Up Redirectors")) - .StepDesc(LOCTEXT("FixUpRedirectsStepDesc", "Resave all packages that point to redirectors in your project, and delete those redirectors if able to resave all the things referencing them.")) - .Icon(FLinterStyle::Get()->GetBrush("Linter.Step.FixUpRedirects.Thumbnail")) - .ShowStepStatusIcon(false) - .StepStatus_Lambda([this]() { return FixUpRedirectorStatus; }) - .StepActionText(LOCTEXT("FixUpRedirectsStepAction", "Fix Up Redirectors")) - .OnPerformAction_Lambda([this](FScopedSlowTask& ScopedSlowTask) - { - FixUpRedirectorStatus = EStepStatus::InProgress; - bool bSuccess = true; - - FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked(TEXT("AssetRegistry")); - ScopedSlowTask.EnterProgressFrame(0, LOCTEXT("Linter.FixUpRedirects.FindingRedirectors", "Looking For redirectors...")); - - // Form a filter from the paths - FARFilter Filter; - Filter.bRecursivePaths = true; - Filter.PackagePaths.Add("/Game"); - Filter.ClassNames.Add("ObjectRedirector"); - - // Query for a list of assets in the selected paths - TArray AssetList; - AssetRegistryModule.Get().GetAssets(Filter, AssetList); - - if (AssetList.Num() > 0) - { - TArray ObjectPaths; - for (const auto& Asset : AssetList) - { - ObjectPaths.Add(Asset.ObjectPath.ToString()); - } - - ScopedSlowTask.EnterProgressFrame(0.25f, LOCTEXT("Linter.FixUpRedirects.LoadingRedirectors", "Loading redirectors...")); - - TArray Objects; - if (LoadAssetsIfNeeded(ObjectPaths, Objects)) - { - // Transform Objects array to ObjectRedirectors array - TArray Redirectors; - for (auto Object : Objects) - { - auto Redirector = CastChecked(Object); - Redirectors.Add(Redirector); - } - - ScopedSlowTask.EnterProgressFrame(0.25f, LOCTEXT("Linter.FixUpRedirects.LoadingRedirectors", "Fixing up redirectors...")); - - // Load the asset tools module - FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked(TEXT("AssetTools")); - AssetToolsModule.Get().FixupReferencers(Redirectors); - } - else - { - FNotificationInfo NotificationInfo(LOCTEXT("FixUpRedirectorsFailed", "Linter failed to load an object redirector when trying to fix up all redirectors.")); - NotificationInfo.ExpireDuration = 6.0f; - NotificationInfo.Hyperlink = FSimpleDelegate::CreateStatic([]() { FMessageLog("LoadErrors").Open(EMessageSeverity::Info, true); }); - NotificationInfo.HyperlinkText = LOCTEXT("LoadObjectHyperlink", "Show Message Log"); - FSlateNotificationManager::Get().AddNotification(NotificationInfo); - bSuccess = false; - } - } - - FixUpRedirectorStatus = bSuccess ? EStepStatus::Success : EStepStatus::Error; - }) - ] - // Build Lighting Widget - + SVerticalBox::Slot() - .Padding(PaddingAmount) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(PaddingAmount) - .Visibility_Lambda([&](){ return (MapAssetDataList.Num() > 0) ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; }) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(PaddingAmount) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SHorizontalBox) - // Template thumbnail image - + SHorizontalBox::Slot() - .Padding(4.0) - .AutoWidth() - .VAlign(VAlign_Top) - [ - SNew(SImage) - .Image(FLinterStyle::Get()->GetBrush("Linter.Step.BuildLighting.Thumbnail")) - ] - // Template name and description - + SHorizontalBox::Slot() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(STextBlock) - .Text(LOCTEXT("BuildLightingStepName", "Build Lighting and Run Map Check")) - .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") - ] - - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SRichTextBlock) - .Text(LOCTEXT("BuildLightingStepDesc", "Open each map listed below and click Map Check in the Build Options Menu of the Level Editor Toolbar. If any Map Check errors are generated, resolve them before packaging your project. If any 'LIGHTING NEEDS TO BE REBUILT' errors are present press Ctrl+Shift+Semicolon to rebuild lighting.")) - .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") - .AutoWrapText(true) - ] - ] - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(SSeparator) - ] - + SVerticalBox::Slot() - .VAlign(VAlign_Fill) - .FillHeight(1.0f) - .Padding(PaddingAmount) - [ - SAssignNew(MarketplaceRecommendationMapScrollBoxPtr, SScrollBox) - .ScrollBarAlwaysVisible(true) - ] - ] - ] - ] - // Save All Step - + SVerticalBox::Slot() - .Padding(PaddingAmount) - .AutoHeight() - [ - SNew(SStepWidget) - .StepName(LOCTEXT("SaveAllStepName", "Save All")) - .StepDesc(LOCTEXT("SaveAllStepDesc", "Save all unsaved levels and assets to disk.")) - .Icon(FLinterStyle::Get()->GetBrush("Linter.Step.SaveAll.Thumbnail")) - .ShowStepStatusIcon(false) - .StepStatus_Lambda([this]() { return SaveAllStatus; }) - .StepActionText(LOCTEXT("SaveAllStepNameAction", "Save All")) - .OnPerformAction_Lambda([this](FScopedSlowTask& ScopedSlowTask) - { - SaveAllStatus = EStepStatus::InProgress; - - // Taken from MainFrameActions.cpp SaveAll - const bool bPromptUserToSave = false; - const bool bSaveMapPackages = true; - const bool bSaveContentPackages = true; - const bool bFastSave = false; - const bool bNotifyNoPackagesSaved = false; - const bool bCanBeDeclined = false; - if (FEditorFileUtils::SaveDirtyPackages(bPromptUserToSave, bSaveMapPackages, bSaveContentPackages, bFastSave, bNotifyNoPackagesSaved, bCanBeDeclined)) - { - SaveAllStatus = EStepStatus::Success; - } - else - { - FNotificationInfo NotificationInfo(LOCTEXT("SaveAllFailed", "Linter failed to save all dirty assets!")); - NotificationInfo.ExpireDuration = 6.0f; - NotificationInfo.Hyperlink = FSimpleDelegate::CreateStatic([]() { FMessageLog("LoadErrors").Open(EMessageSeverity::Info, true); }); - NotificationInfo.HyperlinkText = LOCTEXT("LoadObjectHyperlink", "Show Message Log"); - FSlateNotificationManager::Get().AddNotification(NotificationInfo); - SaveAllStatus = EStepStatus::Error; - } - }) - ] - // Package Project step - + SVerticalBox::Slot() - .Padding(PaddingAmount) - .AutoHeight() - [ - SNew(SBorder) - .Visibility(EVisibility::Collapsed) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(PaddingAmount) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(PaddingAmount) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .Padding(4.0) - .AutoWidth() - .VAlign(VAlign_Top) - [ - SNew(SImage) - .Image(FLinterStyle::Get()->GetBrush("Linter.Step.Package.Thumbnail")) - ] - + SHorizontalBox::Slot() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(STextBlock) - .Text(LOCTEXT("PackageProduct", "Package Product to .Zip")) - .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") - ] - - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SRichTextBlock) - .Text(LOCTEXT("PackageProductDesc", "Upload this .zip file to a hosting site (GoogleDrive/Dropbox/etc.) and enter the download URL as the associated product's Project File Link in the Publisher Portal.")) - .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") - .AutoWrapText(true) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SButton) - .OnClicked_Lambda([&]() - { - #if PLATFORM_WINDOWS - FText PlatformName = LOCTEXT("PlatformName_Windows", "Windows"); - #elif PLATFORM_MAC - FText PlatformName = LOCTEXT("PlatformName_Mac", "Mac"); - #elif PLATFORM_LINUX - FText PlatformName = LOCTEXT("PlatformName_Linux", "Linux"); - #else - FText PlatformName = LOCTEXT("PlatformName_Other", "Other OS"); - #endif - - bool bOpened = false; - TArray SaveFilenames; - IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); - if (DesktopPlatform != NULL) - { - bOpened = DesktopPlatform->SaveFileDialog( - NULL, - NSLOCTEXT("UnrealEd", "ZipUpProject", "Zip file location").ToString(), - FPaths::ProjectDir(), - FApp::GetProjectName(), - TEXT("Zip file|*.zip"), - EFileDialogFlags::None, - SaveFilenames); - } - - // We never want to compile editor targets when invoking UAT in this context. - // If we are installed or don't have a compiler, we must assume we have a precompiled UAT. - const TCHAR* UATFlags = (FApp::GetEngineIsPromotedBuild() || FApp::IsEngineInstalled()) - ? TEXT("-nocompile -nocompileeditor") - : TEXT("-nocompileeditor"); - - if (bOpened) - { - for (FString FileName : SaveFilenames) - { - // Ensure path is full rather than relative (for macs) - FString FinalFileName = FPaths::ConvertRelativePathToFull(FileName); - FString ProjectPath = FPaths::IsProjectFilePathSet() ? FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()) : FPaths::RootDir() / FApp::GetProjectName(); - - FString CommandLine = FString::Printf(TEXT("ZipProjectUp %s -project=\"%s\" -install=\"%s\""), UATFlags, *ProjectPath, *FinalFileName); - - IUATHelperModule::Get().CreateUatTask(CommandLine, PlatformName, LOCTEXT("ZipTaskName", "Zipping Up Project"), - LOCTEXT("ZipTaskShortName", "Zip Project Task"), FEditorStyle::GetBrush(TEXT("MainFrame.CookContent"))); - } - - FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab"))->RequestCloseTab(); - } - return FReply::Handled(); - }) - [ - SNew(STextBlock) - .Text(LOCTEXT("SelectOutputFolder", "Package Project into a .zip")) - ] - ] - ] - ] - ] - ] - ] - ] - ] - ] - ] - ] - ] - ]; - - // Determine all levels in the project for wizard purposes - FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); - TArray AssetDatas; - FARFilter Filter; - Filter.ClassNames.Add(UWorld::StaticClass()->GetFName()); - Filter.bRecursivePaths = true; - Filter.PackagePaths.Add(TEXT("/Game")); - AssetRegistryModule.Get().GetAssets(Filter, AssetDatas); - - MapAssetDataList.Empty(); - for (FAssetData Asset : AssetDatas) - { - MapAssetDataList.Add(MakeShareable(new FAssetData(Asset))); - - MarketplaceRecommendationMapScrollBoxPtr.Get()->AddSlot() - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - .Padding(PaddingAmount) - [ - SNew(SAssetLinkWidget) - .AssetData(Asset) - ]; - } -} -END_SLATE_FUNCTION_BUILD_OPTIMIZATION - -void SLintWizard::OnLintReportEntered() -{ - LintReport->Rebuild(CastChecked(SelectedRuleSet->GetAsset())); -} - -void SLintWizard::OnMarketplaceRecommendationsEntered() -{ - bOfferPackage = true; -} - -bool SLintWizard::LoadAssetsIfNeeded(const TArray& ObjectPaths, TArray& LoadedObjects) -{ - bool bAnyObjectsWereLoadedOrUpdated = false; - - // Build a list of unloaded assets - TArray UnloadedObjectPaths; - bool bAtLeastOneUnloadedMap = false; - for (int32 PathIdx = 0; PathIdx < ObjectPaths.Num(); ++PathIdx) - { - const FString& ObjectPath = ObjectPaths[PathIdx]; - - UObject* FoundObject = FindObject(NULL, *ObjectPath); - if (FoundObject) - { - LoadedObjects.Add(FoundObject); - } - else - { - // Unloaded asset, we will load it later - UnloadedObjectPaths.Add(ObjectPath); - if (FEditorFileUtils::IsMapPackageAsset(ObjectPath)) - { - bAtLeastOneUnloadedMap = true; - } - } - } - - // Make sure all selected objects are loaded, where possible - if (UnloadedObjectPaths.Num() > 0) - { - FScopedSlowTask SlowTask(UnloadedObjectPaths.Num(), LOCTEXT("LoadingObjects", "Loading Objects...")); - SlowTask.MakeDialog(); - - GIsEditorLoadingPackage = true; - - const ELoadFlags LoadFlags = LOAD_None; - bool bSomeObjectsFailedToLoad = false; - for (int32 PathIdx = 0; PathIdx < UnloadedObjectPaths.Num(); ++PathIdx) - { - const FString& ObjectPath = UnloadedObjectPaths[PathIdx]; - SlowTask.EnterProgressFrame(1, FText::Format(LOCTEXT("LoadingObjectf", "Loading {0}..."), FText::FromString(ObjectPath))); - - // Load up the object - UObject* LoadedObject = LoadObject(NULL, *ObjectPath, NULL, LoadFlags, NULL); - if (LoadedObject) - { - LoadedObjects.Add(LoadedObject); - } - else - { - bSomeObjectsFailedToLoad = true; - } - - if (GWarn->ReceivedUserCancel()) - { - // If the user has canceled stop loading the remaining objects. We don't add the remaining objects to the failed string, - // this would only result in launching another dialog when by their actions the user clearly knows not all of the - // assets will have been loaded. - break; - } - } - GIsEditorLoadingPackage = false; - - if (bSomeObjectsFailedToLoad) - { - return false; - } - } - - return true; -} diff --git a/Plugins/Linter/Source/Linter/Private/UI/SAssetLinkWidget.cpp b/Plugins/Linter/Source/Linter/Private/UI/SAssetLinkWidget.cpp deleted file mode 100644 index a3ae2e5..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/SAssetLinkWidget.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#include "UI/SAssetLinkWidget.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SBorder.h" -#include "Widgets/Layout/SBox.h" -#include "Widgets/Images/SImage.h" -#include "Widgets/Input/SButton.h" -#include "Widgets/Text/STextBlock.h" -#include "Widgets/Images/SThrobber.h" -#include "Widgets/Text/SRichTextBlock.h" - -BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION -void SAssetLinkWidget::Construct(const FArguments& Args) -{ - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - AssetData = Args._AssetData; - - ChildSlot - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SHyperlink) - .Text(FText::FromName(AssetData.Get().AssetName)) - .OnNavigate_Lambda([&]() - { - FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); - TArray AssetDatas; - AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(AssetData.Get().ObjectPath)); - ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); - }) - ] - ]; -} -END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Plugins/Linter/Source/Linter/Private/UI/SStepWidget.cpp b/Plugins/Linter/Source/Linter/Private/UI/SStepWidget.cpp deleted file mode 100644 index 28536e8..0000000 --- a/Plugins/Linter/Source/Linter/Private/UI/SStepWidget.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#include "UI/SStepWidget.h" -#include "Widgets/SBoxPanel.h" -#include "Widgets/Layout/SBorder.h" -#include "Widgets/Layout/SBox.h" -#include "Widgets/Images/SImage.h" -#include "Widgets/Input/SButton.h" -#include "Widgets/Text/STextBlock.h" -#include "Widgets/Images/SThrobber.h" -#include "Widgets/Text/SRichTextBlock.h" - -bool SStepWidget::IsStepCompleted(bool bAllowWarning /*= true*/) -{ - EStepStatus Status = StepStatus.Get(); - if (bAllowWarning && Status == EStepStatus::Warning) - { - return true; - } - - return Status == EStepStatus::Success; -} - -void SStepWidget::Construct(const FArguments& Args) -{ - const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); - - StepStatus = Args._StepStatus; - OnPerformAction = Args._OnPerformAction; - StepActionText = Args._StepActionText; - ShowStepStatusIcon = Args._ShowStepStatusIcon; - - // Visibility lambda based on whether step is in progress - auto VisibleIfInProgress = [this]() - { - return StepStatus.Get(EStepStatus::NoStatus) == EStepStatus::InProgress ? EVisibility::Visible : EVisibility::Collapsed; - }; - - // Enabled lambda based on whether this widget has a step status that requires action - auto EnabledBasedOnStepStatus = [this]() -> bool - { - switch (StepStatus.Get(EStepStatus::NoStatus)) - { - case EStepStatus::NoStatus: - case EStepStatus::InProgress: - case EStepStatus::Success: - return false; - case EStepStatus::Unknown: - case EStepStatus::Warning: - case EStepStatus::Error: - case EStepStatus::NeedsUpdate: - return true; - } - return false; - }; - - ChildSlot - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("NoBorder")) - .Padding(PaddingAmount) - [ - SNew(SBorder) - .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) - .Padding(PaddingAmount) - [ - SNew(SHorizontalBox) - // Status Image - + SHorizontalBox::Slot() - .Padding(PaddingAmount) - .AutoWidth() - [ - SNew(SImage) - .Visibility_Lambda([&]() { return StepStatus.Get(EStepStatus::NoStatus) == EStepStatus::NoStatus || !ShowStepStatusIcon.Get(true) ? EVisibility::Collapsed : EVisibility::Visible; }) - .Image_Lambda([&]() - { - switch (StepStatus.Get(EStepStatus::NoStatus)) - { - case NoStatus: - case Unknown: - return FLinterStyle::Get()->GetBrush("Linter.Step.Unknown"); - case InProgress: - case NeedsUpdate: - return FLinterStyle::Get()->GetBrush("Linter.Step.Working"); - case Warning: - return FLinterStyle::Get()->GetBrush("Linter.Step.Warning"); - case Error: - return FLinterStyle::Get()->GetBrush("Linter.Step.Error"); - case Success: - return FLinterStyle::Get()->GetBrush("Linter.Step.Good"); - } - - return FLinterStyle::Get()->GetBrush("Linter.Step.Unknown"); - }) - ] - // Template thumbnail image - + SHorizontalBox::Slot() - .Padding(4.0) - .AutoWidth() - .VAlign(VAlign_Top) - [ - SNew(SImage) - .Visibility(Args._Icon.IsSet() ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed) - .Image(Args._Icon) - ] - // Template name and description - + SHorizontalBox::Slot() - [ - SNew(SVerticalBox) - - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(STextBlock) - .Text(Args._StepName) - .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") - ] - - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SRichTextBlock) - .Text(Args._StepDesc) - .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") - .AutoWrapText(true) - ] - + SVerticalBox::Slot() - .AutoHeight() - .Padding(PaddingAmount) - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SButton) - .IsEnabled_Lambda(EnabledBasedOnStepStatus) - .Visibility_Lambda([&]() { return StepStatus.Get(EStepStatus::NoStatus) == EStepStatus::NoStatus ? EVisibility::Collapsed : EVisibility::Visible; }) - .OnClicked_Lambda([&]() - { - FScopedSlowTask SlowTask(1.0f, StepActionText.Get(FText())); - SlowTask.MakeDialog(); - - OnPerformAction.ExecuteIfBound(SlowTask); - return FReply::Handled(); - }) - [ - SNew(STextBlock) - .Text(StepActionText) - ] - ] - + SHorizontalBox::Slot() - .AutoWidth() - [ - SNew(SThrobber) - .Visibility_Lambda(VisibleIfInProgress) - ] - ] - ] - ] - ] - ]; -} diff --git a/Plugins/Linter/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h b/Plugins/Linter/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h deleted file mode 100644 index 1b7d516..0000000 --- a/Plugins/Linter/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2016 Gamemakin LLC. All Rights Reserved. - -#pragma once - -#include "EditorStyleSet.h" -#include "Widgets/Input/SEditableTextBox.h" -#include "Widgets/Input/SCheckBox.h" -#include "Widgets/SWindow.h" -#include "Widgets/SUserWidget.h" -#include "Widgets/SCompoundWidget.h" -#include "Widgets/Layout/SUniformGridPanel.h" -#include "Widgets/Layout/SSeparator.h" - -#define LOCTEXT_NAMESPACE "LinterBatchRenamer" - -/** -* FDlgBatchRenameTool -* -* Wrapper class for SDlgBatchRenameTool. This class creates and launches a dialog then awaits the -* result to return to the user. -*/ -class FDlgBatchRenameTool -{ -public: - enum EResult - { - Cancel = 0, // No/Cancel, normal usage would stop the current action - Confirm = 1, // Yes/Ok/Etc, normal usage would continue with action - }; - - FDlgBatchRenameTool(const TArray Assets); - - /** Shows the dialog box and waits for the user to respond. */ - EResult ShowModal(); - - FString Prefix; - FString Suffix; - bool bRemovePrefix; - bool bRemoveSuffix; - - FString Find; - FString Replace; - -private: - - /** Cached pointer to the modal window */ - TSharedPtr DialogWindow; - - /** Cached pointer to the batch rename tool widget */ - TSharedPtr DialogWidget; - - const TArray SelectedAssets; -}; - -/** -* Slate panel for batch renaming -*/ -class SDlgBatchRenameTool : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SDlgBatchRenameTool) - {} - /** Window in which this widget resides */ - SLATE_ATTRIBUTE(TSharedPtr, ParentWindow) - SLATE_END_ARGS() - - /** - * Constructs this widget - * - * @param InArgs The declaration data for this widget - */ - void Construct(const FArguments& InArgs); - - /** - * Returns the EResult of the button which the user pressed. Closing of the dialog - * in any other way than clicking "Ok" results in this returning a "Cancel" value - */ - FDlgBatchRenameTool::EResult GetUserResponse() const; - -private: - - /** - * Handles when a button is pressed, should be bound with appropriate EResult Key - * - * @param ButtonID - The return type of the button which has been pressed. - */ - FReply OnButtonClick(FDlgBatchRenameTool::EResult ButtonID) - { - ParentWindow->RequestDestroyWindow(); - UserResponse = ButtonID; - - return FReply::Handled(); - } - - /** Stores the users response to this dialog */ - FDlgBatchRenameTool::EResult UserResponse; - - /** Pointer to the window which holds this Widget, required for modal control */ - TSharedPtr ParentWindow; - -public: - - TSharedPtr PrefixTextBox; - TSharedPtr SuffixTextBox; - TSharedPtr FindTextBox; - TSharedPtr ReplaceTextBox; - TSharedPtr PrefixRemoveBox; - TSharedPtr SuffixRemoveBox; -}; - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/LintRule.h b/Plugins/Linter/Source/Linter/Public/LintRule.h deleted file mode 100644 index 842f9e5..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRule.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.generated.h" - -UENUM(BlueprintType) -enum class ELintRuleSeverity : uint8 -{ - Error, - Warning, - Info -// Ignore -}; - -USTRUCT(BlueprintType) -struct LINTER_API FLintRuleViolation -{ - GENERATED_USTRUCT_BODY() - - FLintRuleViolation() - : Violator(nullptr) - , ViolatedRule(nullptr) - , RecommendedAction(FText::GetEmpty()) - { - } - - FLintRuleViolation(UObject* InViolator, TSubclassOf InViolatedRule, const FText InRecommendedAction = FText::GetEmpty()) - : Violator(InViolator) - , ViolatedRule(InViolatedRule) - , RecommendedAction(InRecommendedAction) - { - } - - // I don't particularly like this way of extracting relevant data, but alas here we are. - static TArray AllRuleViolationsWithViolator(const TArray& RuleViolationCollection, const UObject* SearchViolator); - static TArray> AllRuleViolationsWithViolatorShared(const TArray& RuleViolationCollection, const UObject* SearchViolator); - static TArray> AllRuleViolationsWithViolatorShared(const TArray>& RuleViolationCollection, const UObject* SearchViolator); - static TArray AllRuleViolationsOfSpecificRule(const TArray& RuleViolationCollection, TSubclassOf SearchRule); - static TArray AllRuleViolationsOfRuleGroup(const TArray& RuleViolationCollection, FName SearchRuleGroup); - - static TArray AllRuleViolationViolators(const TArray& RuleViolationCollection); - static TArray AllRuleViolationViolators(const TArray>& RuleViolationCollection); - static TMultiMap AllRuleViolationsMappedByViolator(const TArray& RuleViolationCollection); - static TMultiMap AllRuleViolationsMappedByViolatedLintRule(const TArray& RuleViolationCollection); - static TMultiMap> AllRuleViolationsMappedByViolatedLintRuleShared(const TArray& RuleViolationCollection); - static TMultiMap> AllRuleViolationsMappedByViolatedLintRuleShared(const TArray>& RuleViolationCollection); - - bool PopulateAssetData(); - - UPROPERTY(EditAnywhere, Category = "Lint") - TWeakObjectPtr Violator; - - UPROPERTY(EditAnywhere, Category = "Lint") - TSubclassOf ViolatedRule; - - UPROPERTY(EditAnywhere, Category = "Lint") - FText RecommendedAction; - - FAssetData ViolatorAssetData; -}; - -/** - *Comment - */ -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule : public UObject -{ - GENERATED_BODY() - -public: - - ULintRule(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Display") - FName RuleGroup; - - UPROPERTY(EditDefaultsOnly, Category = "Display") - FText RuleTitle; - - UPROPERTY(EditDefaultsOnly, Category = "Display") - FText RuleDescription; - - UPROPERTY(EditDefaultsOnly, Category = "Display") - FString RuleURL; - - UPROPERTY(EditDefaultsOnly, Category = "Display") - ELintRuleSeverity RuleSeverity; - - UPROPERTY(EditDefaultsOnly, Category = "Settings", AdvancedDisplay) - bool bRequiresGameThread = false; - - UFUNCTION(BlueprintCallable, Category = "Lint") - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; - - UFUNCTION(BlueprintCallable, Category = "Display") - virtual bool IsRuleSuppressed() const; - - UFUNCTION(BlueprintNativeEvent, Category = "Display") - FName GetRuleBasedObjectVariantName(UObject* ObjectToLint) const; - -protected: - - /* This is the function that child lint rules should override to perform the meat of the rule check - * You do not call this directly. Always call PassesRule. PassesRule forwards to the PassesRule_Internal ONLY IF - * data is valid and the rule is not suppressed, therefore it is worth checking. - */ - UFUNCTION(BlueprintNativeEvent, Category = "Lint") - bool PassesRule_Internal(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; - -private: - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRuleSet.h b/Plugins/Linter/Source/Linter/Public/LintRuleSet.h deleted file mode 100644 index d5e16f9..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRuleSet.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "Misc/ScopedSlowTask.h" -#include "LintRule.h" - -#include "LintRuleSet.generated.h" - -class ULinterNamingConvention; - -USTRUCT(BlueprintType) -struct LINTER_API FLintRuleList -{ - GENERATED_USTRUCT_BODY() - - FLintRuleList() - {} - - UPROPERTY(EditAnywhere, Category = Default) - TArray> LintRules; - - bool RequiresGameThread() const;; - bool PassesRules(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; -}; - -/** - *Comment - */ -UCLASS(BlueprintType, Blueprintable) -class LINTER_API ULintRuleSet : public UDataAsset -{ - GENERATED_BODY() - -public: - ULintRuleSet(const FObjectInitializer& ObjectInitializer); - - //UFUNCTION(BlueprintCallable, Category = "Conventions") - const FLintRuleList* GetLintRuleListForClass(TSoftClassPtr Class) const; - - UFUNCTION(BlueprintCallable, Category = "Conventions") - ULinterNamingConvention* GetNamingConvention() const; - - /** Invoke this with a list of asset paths to recursively lint all assets in paths. */ - //UFUNCTION(BlueprintCallable, Category = "Lint") - TArray LintPath(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask = nullptr) const; - - /** This is a temp dumb way to do this. */ - TArray> LintPathShared(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask = nullptr) const; - - UPROPERTY(EditDefaultsOnly, Category = "Marketplace") - bool bShowMarketplacePublishingInfoInLintWizard = false; - - UPROPERTY(EditDefaultsOnly, Category = "Rules") - FText RuleSetDescription; - - UPROPERTY(EditDefaultsOnly, Category = "Commandlet") - FString NameForCommandlet; - -protected: - - UPROPERTY(EditDefaultsOnly, Category = "Rules") - TSoftObjectPtr NamingConvention; - - UPROPERTY(EditDefaultsOnly, Category = "Rules") - TMap, FLintRuleList> ClassLintRulesMap; - -}; - diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h deleted file mode 100644 index f42a2b1..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Base.generated.h" - -/** - *Comment - */ -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Base : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Base(const FObjectInitializer& ObjectInitializer); - - // This does rule pre-checks. You probably want to override PassesRule_Internal_Implementation - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h deleted file mode 100644 index b6629ef..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Compiles.generated.h" - -/** - *Comment - */ -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Compiles : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Compiles(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h deleted file mode 100644 index 6b86d84..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Funcs_MaxNodes.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Funcs_MaxNodes : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Funcs_MaxNodes(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - int32 MaxExpectedNonTrivialNodes = 50; - - static bool IsNodeTrivial(const UEdGraphNode* Node); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h deleted file mode 100644 index 0bc90e6..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Funcs_MustHaveReturn.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Funcs_MustHaveReturn : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Funcs_MustHaveReturn(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h deleted file mode 100644 index 6978c6e..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Funcs_PublicDescriptions.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Funcs_PublicDescriptions : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Funcs_PublicDescriptions(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h deleted file mode 100644 index a001355..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_LooseNodes.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_LooseNodes : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_LooseNodes(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h deleted file mode 100644 index ffa268d..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Vars_ConfigCategories.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Vars_ConfigCategories : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - int32 NumVariablesToRequireCategorization = 5; - -public: - ULintRule_Blueprint_Vars_ConfigCategories(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h deleted file mode 100644 index 97dc55c..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Vars_EditableMustHaveTooltip.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Vars_EditableMustHaveTooltip : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Vars_EditableMustHaveTooltip(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h deleted file mode 100644 index d56d84b..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Vars_NoConfigFlag.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Vars_NoConfigFlag : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Vars_NoConfigFlag(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h deleted file mode 100644 index 8342e0a..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Vars_NonAtomic.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Vars_NonAtomic : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Vars_NonAtomic(const FObjectInitializer& ObjectInitializer); - - static bool IsVariableAtomic(FBPVariableDescription& VarDesc); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h deleted file mode 100644 index 8219752..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Vars_PluralArrays.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Vars_PluralArrays : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Vars_PluralArrays(const FObjectInitializer& ObjectInitializer); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h deleted file mode 100644 index 65b52cc..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Blueprint_Vars_Regex.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Blueprint_Vars_Regex : public ULintRule_Blueprint_Base -{ - GENERATED_BODY() - -public: - ULintRule_Blueprint_Vars_Regex(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - bool bUseLowercaseBPrefixForBooleans = true; - - UPROPERTY(EditAnywhere, Category = "Settings") - FString RegexPatternString; - - UPROPERTY(EditAnywhere, Category = "Settings") - bool bMustNotContainRegexPattern = true; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Collection.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Collection.h deleted file mode 100644 index cbd39a0..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Collection.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Collection.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Collection : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Collection(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - TArray> SubRules; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h deleted file mode 100644 index abb820a..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" -#include "LintRule_IsNamedCorrectly_Base.generated.h" - - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_IsNamedCorrectly_Base : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_IsNamedCorrectly_Base(const FObjectInitializer& ObjectInitializer); - - UFUNCTION(BlueprintCallable, Category="Lint") - static FString BuildSuggestedName(FString CurrentName, FString DesiredPrefix, FString DesiredSuffix = TEXT("")); - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h deleted file mode 100644 index 73069a6..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_ParticleSystem_EmitterNameRegex.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_ParticleSystem_EmitterNameRegex : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_ParticleSystem_EmitterNameRegex(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditAnywhere, Category = "Settings") - int32 MinEmittersNeededToEnforce = 2; - - UPROPERTY(EditAnywhere, Category="Settings") - FString RegexPatternString; - - UPROPERTY(EditAnywhere, Category = "Settings") - bool bMustNotContainRegexPattern = true; - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - FText DisallowedRecommendedAction; - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - FText NonConformingRecommendedAction; - - - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h deleted file mode 100644 index ec5d2ad..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Path_DisallowNames.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Path_DisallowNames : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Path_DisallowNames(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - TArray DisallowedFolderNames; - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - FText RecommendedAction; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h deleted file mode 100644 index 36be61e..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Path_IsNotTooLong.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Path_IsNotTooLong : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Path_IsNotTooLong(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - int32 MaxPathLimit = 140; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h deleted file mode 100644 index 8c423e9..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Path_NoTopLevel.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Path_NoTopLevel : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Path_NoTopLevel(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Display") - FText ZeroTopLevelFoldersRecommendedAction; - - UPROPERTY(EditDefaultsOnly, Category = "Display") - FText PleaseUseThisFolderRecommendedAction; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_Regex.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_Regex.h deleted file mode 100644 index e12c751..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_Regex.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Path_Regex.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Path_Regex : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Path_Regex(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditAnywhere, Category="Settings") - FString RegexPatternString; - - UPROPERTY(EditAnywhere, Category = "Settings") - bool bMustNotContainRegexPattern = true; - - UPROPERTY(EditAnywhere, Category = "Settings") - bool bCheckPerPathElement = true; - - UPROPERTY(EditDefaultsOnly, Category = "Settings|Whole Path") - FText DisallowedWholePathRecommendedAction; - - UPROPERTY(EditDefaultsOnly, Category = "Settings|Whole Path") - FText NonConformingWholePathRecommendedAction; - - UPROPERTY(EditDefaultsOnly, Category = "Settings|Path Element") - FText DisallowedPathElementRecommendedAction; - - UPROPERTY(EditDefaultsOnly, Category = "Settings|Path Element") - FText NonConformingPathElementRecommendedAction; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h deleted file mode 100644 index a801903..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_SoundWave_SampleRate.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_SoundWave_SampleRate : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_SoundWave_SampleRate(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - TArray ValidSampleRates; - - - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h deleted file mode 100644 index d794446..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_StaticMesh_ValidUVs.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_StaticMesh_ValidUVs : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_StaticMesh_ValidUVs(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - bool bIgnoreMissingUVs = false; - - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h deleted file mode 100644 index 4b8ae66..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" - -#include "LintRule_Texture_Size_NotTooBig.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Texture_Size_NotTooBig : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Texture_Size_NotTooBig(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - int32 MaxTextureSizeX = 8192; - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - int32 MaxTextureSizeY = 8192; - - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h b/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h deleted file mode 100644 index d5b4e67..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "LintRule.h" -#include "Engine/TextureDefines.h" - -#include "LintRule_Texture_Size_PowerOfTwo.generated.h" - -UCLASS(BlueprintType, Blueprintable, Abstract) -class LINTER_API ULintRule_Texture_Size_PowerOfTwo : public ULintRule -{ - GENERATED_BODY() - -public: - ULintRule_Texture_Size_PowerOfTwo(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditDefaultsOnly, Category = "Settings") - TSet> IgnoreTexturesInTheseGroups; - - virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -protected: - virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LintRunner.h b/Plugins/Linter/Source/Linter/Public/LintRunner.h deleted file mode 100644 index 0150aa3..0000000 --- a/Plugins/Linter/Source/Linter/Public/LintRunner.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "CoreMinimal.h" -#include "HAL/ThreadSafeCounter.h" -#include "HAL/Runnable.h" -#include "AssetData.h" -#include "Linter.h" - -class FLintRunner : public FRunnable -{ - -public: - - FLintRunner(UObject* InLoadedObject, const ULintRuleSet* LintRuleSet, TArray* InpOutRuleViolations, FScopedSlowTask* InParentScopedSlowTask); - - virtual bool RequiresGamethread(); - - virtual bool Init() override; - virtual uint32 Run() override; - virtual void Stop() override; - virtual void Exit() override; - -protected: - UObject* LoadedObject = nullptr; - const ULintRuleSet* RuleSet = nullptr; - TArray* pOutRuleViolations; - - const FLintRuleList* pLoadedRuleList; - static FCriticalSection LintDataUpdateLock; - - FScopedSlowTask* ParentScopedSlowTask; -}; - diff --git a/Plugins/Linter/Source/Linter/Public/Linter.h b/Plugins/Linter/Source/Linter/Public/Linter.h deleted file mode 100644 index a9dfb17..0000000 --- a/Plugins/Linter/Source/Linter/Public/Linter.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#pragma once - -#include "Modules/ModuleManager.h" - -#include "Widgets/Docking/SDockTab.h" -#include "Styling/SlateStyle.h" - -class FLinterManagerBase; - -DECLARE_LOG_CATEGORY_EXTERN(LogLinter, Verbose, All); -DECLARE_LOG_CATEGORY_EXTERN(LogCommandlet, All, All); - -class LINTER_API FLinterModule : public IModuleInterface -{ -public: - - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; - - static TSharedRef SpawnTab(const FSpawnTabArgs& TabSpawnArgs, TSharedPtr StyleSet); - - virtual bool SupportsDynamicReloading() override - { - return false; - } - - virtual TArray GetDesiredLintPaths() - { - if (DesiredLintPaths.Num() == 0) - { - DesiredLintPaths.Push(TEXT("/Game")); - } - - return DesiredLintPaths; - } - virtual void SetDesiredLintPaths(TArray LintPaths) - { - DesiredLintPaths = LintPaths; - if (DesiredLintPaths.Num() == 0) - { - DesiredLintPaths.Push(TEXT("/Game")); - } - } - -private: - FDelegateHandle LevelEditorTabManagerChangedHandle; - FDelegateHandle ContentBrowserExtenderDelegateHandle; - FDelegateHandle AssetExtenderDelegateHandle; - - TArray DesiredLintPaths; -public: - void OnInitialAssetRegistrySearchComplete(); - static void TryToLoadAllLintRuleSets(); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/LinterBase.h b/Plugins/Linter/Source/Linter/Public/LinterBase.h deleted file mode 100644 index 2a0d36e..0000000 --- a/Plugins/Linter/Source/Linter/Public/LinterBase.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#pragma once -#include "Internationalization/Regex.h" -#include "CoreMinimal.h" -#include "UObject/Class.h" -#include "UObject/Object.h" - -/** Future Notes: - * The data structure for Linter was originally pretty straight forward. Now that we are introducing - * some 'smart' behavior to some of the errors i.e. arbitrary automated resolve actions - * and also tabulating data in both asset first and rule first orders, instead of being a - * set of loosely defined rules, rules should ideally become a strongly typed const table of some kind. - * This would simplify a lot of the data and structs being used here. -- Allar -**/ - -struct LINTER_API FLinterAssetError -{ - /** User friendly error message ready for displaying. */ - FText ErrorMessage; - - /** URL Link to link user to if they want more information on this error. */ - FString URLLink; - - FLinterAssetError(FText InErrorMessage, FString InURLLink = TEXT("")) - : ErrorMessage(InErrorMessage) - , URLLink(InURLLink) - { - } -}; - -struct LINTER_API FLinterAssetErrorList -{ - FString AssetName; - FString AssetPath; - FString SuggestedAssetName; - TArray> Errors; - TArray> Warnings; - - - FLinterAssetErrorList() {}; - - FLinterAssetErrorList(FString InAssetName, FString InAssetPath, TArray> InErrors, TArray> InWarnings, FString InSuggestedAssetName = TEXT("")) - : AssetName(InAssetName) - , AssetPath(InAssetPath) - , SuggestedAssetName(InSuggestedAssetName) - , Errors(InErrors) - , Warnings(InWarnings) - { - } - - FLinterAssetErrorList(const UObject* Object, TArray> InErrors, TArray> InWarnings, FString InSuggestedAssetName = TEXT("")) - : AssetName(Object->GetName()) - , AssetPath(Object->GetPathName()) - , SuggestedAssetName(InSuggestedAssetName) - , Errors(InErrors) - , Warnings(InWarnings) - - { - } - - void Reset() - { - AssetName.Empty(); - AssetPath.Empty(); - SuggestedAssetName.Empty(); - Errors.Empty(); - Warnings.Empty(); - } - - bool IsEmpty() - { - return Errors.Num() == 0 && Warnings.Num() == 0; - } - - bool HasErrors() - { - return Errors.Num() != 0; - } -}; - -/* Linter was originally built to store its data on a per-asset basis. - * After Epic's purchase, UI needed a way to store data on a per-rule basis. - * The following structs help out the LinterManager populate a per-rule list after Linting is complete. */ - -struct LINTER_API FLinterAssetInfo -{ - FString AssetName; - FString AssetPath; - FString SuggestedAssetName; - - FText RuleErrorContext; - - FLinterAssetInfo(const UObject* Object, FText InRuleErrorContext = FText::GetEmpty(), FString InSuggestedAssetName = TEXT("")) - { - AssetName = Object->GetName(); - AssetPath = Object->GetPathName(); - SuggestedAssetName = InSuggestedAssetName; - } -}; - -struct LINTER_API FLinterRuleErrorList -{ - FText RuleMessage; - FString RuleURL; - bool bWarning; - TArray> AssetInfos; - - FLinterRuleErrorList() - : bWarning(false) - { - } - - FLinterRuleErrorList(FText InRuleMessage, FText InRuleContext, bool bInWarning, const UObject* Object, FString InRuleURL, FString InSuggestedAssetName = TEXT("")) - : RuleMessage(InRuleMessage) - , RuleURL(InRuleURL) - , bWarning(bInWarning) - { - AssetInfos.Add(MakeShareable(new FLinterAssetInfo(Object, InRuleContext, InSuggestedAssetName))); - } -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/LinterCommandlet.h b/Plugins/Linter/Source/Linter/Public/LinterCommandlet.h deleted file mode 100644 index 3fa342e..0000000 --- a/Plugins/Linter/Source/Linter/Public/LinterCommandlet.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 Gamemakin LLC. All Rights Reserved. - -#pragma once -#include "Commandlets/Commandlet.h" -#include "LinterCommandlet.generated.h" - -UCLASS() -class ULinterCommandlet : public UCommandlet -{ - GENERATED_UCLASS_BODY() - //~ Begin UCommandlet Interface - virtual int32 Main(const FString& Params) override; - - //~ End UCommandlet Interface -}; - - diff --git a/Plugins/Linter/Source/Linter/Public/LinterContentBrowserExtensions.h b/Plugins/Linter/Source/Linter/Public/LinterContentBrowserExtensions.h deleted file mode 100644 index 463c779..0000000 --- a/Plugins/Linter/Source/Linter/Public/LinterContentBrowserExtensions.h +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#pragma once - - -// Integrate Linter actions into the Content Browser -class FLinterContentBrowserExtensions -{ -public: - static void InstallHooks(FLinterModule* LinterModule, class FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle); - static void RemoveHooks(FLinterModule* LinterModule, class FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/LinterNamingConvention.h b/Plugins/Linter/Source/Linter/Public/LinterNamingConvention.h deleted file mode 100644 index d05f017..0000000 --- a/Plugins/Linter/Source/Linter/Public/LinterNamingConvention.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#pragma once -#include "CoreMinimal.h" -#include "UObject/Object.h" -#include "Templates/SharedPointer.h" -#include "IDetailCustomization.h" -#include "PropertyHandle.h" -#include "LinterNamingConvention.generated.h" - - -/** - * Class/Prefix/Suffix settings for Linter - */ -USTRUCT(BlueprintType) -struct LINTER_API FLinterNamingConventionInfo -{ - GENERATED_USTRUCT_BODY() - - FLinterNamingConventionInfo() - : SoftClassPtr(nullptr) - {} - - FLinterNamingConventionInfo(TSoftClassPtr InClass, FString InPrefix = TEXT(""), FString InSuffix = TEXT(""), FName InVariant = NAME_None) - : SoftClassPtr(InClass) - , Prefix(InPrefix) - , Suffix(InSuffix) - , Variant(InVariant) - {} - - UPROPERTY(EditAnywhere, Category = Default, meta = (AllowAbstract = "")) - TSoftClassPtr SoftClassPtr; - - UPROPERTY(EditAnywhere, Category = Default) - FString Prefix; - - UPROPERTY(EditAnywhere, Category = Default) - FString Suffix; - - UPROPERTY(EditAnywhere, Category = Default) - FName Variant; -}; - -class FLinterNamingConventionDetails : public IDetailCustomization -{ -public: - /** Makes a new instance of this detail layout class for a specific detail view requesting it */ - static TSharedRef MakeInstance(); - - /** ILayoutDetails interface */ - virtual void CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) override; - - void OnGenerateElementForDetails(TSharedRef StructProperty, int32 ElementIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout); -}; - -/** -* Contains a naming convention to be used by LinterManagers/LinterRules -*/ -UCLASS(Abstract) -class LINTER_API ULinterNamingConvention : public UDataAsset -{ - GENERATED_BODY() - -public: - - ULinterNamingConvention(const FObjectInitializer& ObjectInitializer); - - UPROPERTY(EditAnywhere, Category="Conventions", meta = (AllowAbstract = "")) - TArray ClassNamingConventions; - - UFUNCTION(BlueprintCallable, Category="Conventions") - TArray GetNamingConventionsForClassVariant(TSoftClassPtr Class, FName Variant = NAME_None) const; - - UFUNCTION(Blueprintcallable, Category = "Conventions") - void SortConventions(); - -protected: - - - -}; diff --git a/Plugins/Linter/Source/Linter/Public/LinterStyle.h b/Plugins/Linter/Source/Linter/Public/LinterStyle.h deleted file mode 100644 index 236384f..0000000 --- a/Plugins/Linter/Source/Linter/Public/LinterStyle.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. - -#pragma once -#include "CoreTypes.h" -#include "Styling/ISlateStyle.h" - -class FLinterStyle -{ -public: - static void Initialize(); - - static void Shutdown(); - - static TSharedPtr< class ISlateStyle > Get(); - static TSharedPtr< class FSlateStyleSet > StyleSet; - - static FName GetStyleSetName(); -private: - static FString InContent(const FString& RelativePath, const ANSICHAR* Extension); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h b/Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h deleted file mode 100644 index 0fc717d..0000000 --- a/Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "Internationalization/Regex.h" -#include "CoreMinimal.h" -#include "UObject/Class.h" -#include "UObject/Object.h" - -/** -* Helper struct for showing function tooltip widgets -**/ -struct FBPFunctionArgumentDescription -{ - FText ArgumentName; - FText Tooltip; - FText ArgumentType; - - FBPFunctionArgumentDescription() - { - } - - FBPFunctionArgumentDescription(FText InArgumentName, FText InTooltip, FText InArgumentType = FText::GetEmpty()) - : ArgumentName(InArgumentName) - , Tooltip(InTooltip) - , ArgumentType(InArgumentType) - { - } - - FText GetToolTipTextRef() - { - return Tooltip; - } -}; - -class FTooltipStringHelper -{ -public: - static FText ParseFunctionRawTooltipGetDescription(FString RawTooltip, bool bRemoveNewlines = false); - static bool ParseFunctionRawTooltip(FString RawTooltip, FText& OutFunctionDescription, TArray>& Inputs, TArray>& Outputs, FText& OutReturnText); - static FString ConvertTooltipDataToRawTooltip(FText FunctionDescription, TArray> Inputs, TArray> Outputs); - static bool FindAndUpdateArgumentTooltip(FText ArgumentName, FText Tooltip, TArray>& Arguments); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipTool.h b/Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipTool.h deleted file mode 100644 index 5711675..0000000 --- a/Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipTool.h +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2016 Gamemakin LLC. All Rights Reserved. - -#pragma once - -#include "EditorStyleSet.h" - -#include "Widgets/SCompoundWidget.h" -#include "Widgets/SUserWidget.h" -#include "Widgets/SWindow.h" -#include "Widgets/Layout/SSeparator.h" -#include "Widgets/Layout/SUniformGridPanel.h" -#include "Widgets/Input/SEditableTextBox.h" -#include "Widgets/Input/SCheckBox.h" -#include "Widgets/Input/SComboBox.h" -#include "Widgets/Input/SMultiLineEditableTextBox.h" -#include "Widgets/Views/SListView.h" -#include "AssetData.h" -#include "Engine/Blueprint.h" -#include "K2Node_FunctionEntry.h" -#include "K2Node_FunctionResult.h" - -#include "TooltipStringHelper.h" - -#define LOCTEXT_NAMESPACE "LinterTooltipTool" - - -/** -* Helper struct for showing function tooltip widgets -**/ -struct FBPFunctionPointers -{ - UK2Node_FunctionEntry* FunctionEntryNode; - UK2Node_FunctionResult* FunctionResultNode; - FName FunctionName; - - FBPFunctionPointers() - { - } - - FBPFunctionPointers(UK2Node_FunctionEntry* InFunctionEntryNode, UK2Node_FunctionResult* InFunctionResultNode, FName InFunctionName) - : FunctionEntryNode(InFunctionEntryNode) - , FunctionResultNode(InFunctionResultNode) - , FunctionName(InFunctionName) - { - } -}; - -/** -* FTooltipTool -* -* Wrapper class for STooltipTool. This class creates and launches a dialog then awaits the -* result to return to the user. -*/ -class FTooltipTool -{ -public: - enum EResult - { - Cancel = 0, // No/Cancel, normal usage would stop the current action - Confirm = 1, // Yes/Ok/Etc, normal usage would continue with action - }; - - FTooltipTool(const TArray Assets); - - /** Shows the dialog box and waits for the user to respond. */ - EResult ShowModal(); - - TArray BlueprintsInternal; - TArray> Blueprints; - -private: - - /** Cached pointer to the modal window */ - TSharedPtr DialogWindow; - - /** Cached pointer to the batch rename tool widget */ - TSharedPtr DialogWidget; -}; - -/** -* Slate panel for batch renaming -*/ -class STooltipTool : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(STooltipTool) - {} - /** Window in which this widget resides */ - SLATE_ATTRIBUTE(TSharedPtr, ParentWindow) - SLATE_ATTRIBUTE(TArray>, Blueprints) - SLATE_END_ARGS() - - /** - * Constructs this widget - * - * @param InArgs The declaration data for this widget - */ - void Construct(const FArguments& InArgs); - - /** - * Returns the EResult of the button which the user pressed. Closing of the dialog - * in any other way than clicking "Ok" results in this returning a "Cancel" value - */ - FTooltipTool::EResult GetUserResponse() const; - -private: - - /** - * Handles when a button is pressed, should be bound with appropriate EResult Key - * - * @param ButtonID - The return type of the button which has been pressed. - */ - FReply OnButtonClick(FTooltipTool::EResult ButtonID); - - FText GetSelectedBlueprintText() const; - - void UpdateVariableTooltipText(const FText& NewText); - void UpdateCurrentFunctionTooltipText(); - - void RebuildMemberList(); - - /** Stores the users response to this dialog */ - FTooltipTool::EResult UserResponse; - - /** Pointer to the window which holds this Widget, required for modal control */ - TSharedPtr ParentWindow; - -public: - - - - TAttribute>> Blueprints; - TSharedPtr>> BlueprintComboBox; - - FText CurrentFunctionDescription; - - TArray> FunctionPointers; - TSharedPtr>> FunctionListView; - TSharedPtr FunctionDescriptionTooltipBox; - TArray> FunctionArgumentDescriptions; - TSharedPtr>> FunctionArgumentListView; - TArray> FunctionOutputDescriptions; - TSharedPtr>> FunctionOutputListView; - - - TArray> Members; - TSharedPtr>> MemberListView; - TSharedPtr VariableTooltipEditableTextBox; - - TSharedPtr CommitTextButton; - TSharedPtr CommitOnTextChangeCheckBox; -}; - -#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReport.h b/Plugins/Linter/Source/Linter/Public/UI/LintReport.h deleted file mode 100644 index f157bd2..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReport.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once - -#include "Widgets/SCompoundWidget.h" -#include "Widgets/Layout/SScrollBox.h" - -#include "LintReportAssetError.h" -#include "LintRule.h" - - -class SLintReport : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReport) - { - } - - SLATE_END_ARGS() - -public: - - void Construct(const FArguments& Args); - void Rebuild(const ULintRuleSet* SelectedLintRuleSet); - TSharedRef GetViewButtonContent(); - - const ULintRuleSet* LastUsedRuleSet = nullptr; - - TSharedPtr ResultsTextBlockPtr; - TArray> RuleViolations; - TSharedPtr ViewOptionsComboButton; - TSharedPtr AssetDetailsScrollBoxPtr; - TSharedPtr RuleDetailsScrollBoxPtr; - FString JsonReport; - FString HTMLReport; - - bool bHasRanReport = false; - int32 NumErrors = 0; - int32 NumWarnings = 0; - - -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetDetails.h b/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetDetails.h deleted file mode 100644 index e8934d6..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetDetails.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once -#include "Widgets/SCompoundWidget.h" -#include "LintRule.h" - -class SLintReportAssetDetails : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReportAssetDetails) - { - } - SLATE_ATTRIBUTE(FAssetData, AssetData) - SLATE_ATTRIBUTE(TArray>, RuleViolations) - SLATE_ATTRIBUTE(TSharedPtr, ThumbnailPool) - - SLATE_END_ARGS() - - TAttribute AssetData; - TAttribute>> RuleViolations; - TAttribute> ThumbnailPool; - - -public: - - void Construct(const FArguments& Args); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetError.h b/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetError.h deleted file mode 100644 index 4c4a542..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetError.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once -#include "Widgets/SCompoundWidget.h" -#include "LintRule.h" - -class SLintReportAssetError : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReportAssetError) - { - } - SLATE_ATTRIBUTE(TSharedPtr, RuleViolation) - - SLATE_END_ARGS() - - TAttribute> RuleViolation; - - -public: - - void Construct(const FArguments& Args); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetErrorList.h b/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetErrorList.h deleted file mode 100644 index ec8ac0f..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReportAssetErrorList.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once -#include "Widgets/SCompoundWidget.h" -#include "Widgets/DeclarativeSyntaxSupport.h" - -class SLintReportAssetErrorList : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReportAssetErrorList) - { - } - SLATE_ATTRIBUTE(TArray>, RuleViolations) - - SLATE_END_ARGS() - - TAttribute>> RuleViolations; - -public: - - void Construct(const FArguments& Args); - -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleDetails.h b/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleDetails.h deleted file mode 100644 index fab0e24..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleDetails.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once -#include "Widgets/SCompoundWidget.h" -#include "LintRule.h" - -class SLintReportRuleDetails : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReportRuleDetails) - { - } - SLATE_ATTRIBUTE(TArray>, RuleViolations) - SLATE_ATTRIBUTE(TSharedPtr, ThumbnailPool) - - SLATE_END_ARGS() - - TAttribute>> RuleViolations; - TAttribute> ThumbnailPool; - - -public: - - void Construct(const FArguments& Args); - -private: - FString RuleURL; - FAssetData RuleAssetData; - TSharedPtr ThumbnailBox; -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleError.h b/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleError.h deleted file mode 100644 index dd2890c..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleError.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once -#include "Widgets/SCompoundWidget.h" -#include "LintRule.h" - -class SLintReportRuleError : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReportRuleError) - { - } - SLATE_ATTRIBUTE(TSharedPtr, RuleViolation) - - SLATE_END_ARGS() - - TAttribute> RuleViolation; - - -public: - - void Construct(const FArguments& Args); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleErrorList.h b/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleErrorList.h deleted file mode 100644 index 233d217..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintReportRuleErrorList.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. -#pragma once -#include "Widgets/SCompoundWidget.h" -#include "Widgets/DeclarativeSyntaxSupport.h" - -class SLintReportRuleErrorList : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SLintReportRuleErrorList) - { - } - SLATE_ATTRIBUTE(TArray>, RuleViolations) - - SLATE_END_ARGS() - - TAttribute>> RuleViolations; - -public: - - void Construct(const FArguments& Args); - -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/LintWizard.h b/Plugins/Linter/Source/Linter/Public/UI/LintWizard.h deleted file mode 100644 index fee641d..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/LintWizard.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015-2017 by Gamemakin LLC - -#pragma once - -#include "CoreMinimal.h" -#include "Widgets/SCompoundWidget.h" -#include "Widgets/Workflow/SWizard.h" -#include "UI/SStepWidget.h" - -#include "LintReport.h" - -/** - * - */ -class LINTER_API SLintWizard : public SCompoundWidget -{ -public: - SLATE_BEGIN_ARGS(SLintWizard) - {} - SLATE_END_ARGS() - - /** Constructs this widget with InArgs */ - void Construct(const FArguments& InArgs); - - /** The wizard widget */ - TSharedPtr MainWizard; - - /** The Lint Report widget */ - TSharedPtr LintReport; - - /** The Linter combo box selection to use to select a linter rule set. */ - TSharedPtr>> RuleSetSelectionComboBox; - - /** List of Linter managers grabbed from the Linter Module on widget creation */ - TArray> RuleSets; - - /** List of maps in the project used for wizard purposes */ - TArray> MapAssetDataList; - - /** Scrollbox for list of map assets in marketplace recommendation page */ - TSharedPtr MarketplaceRecommendationMapScrollBoxPtr; - - /** Currently selected Linter Rule Set */ - TSharedPtr SelectedRuleSet; - - bool bOfferPackage = false; - EStepStatus FixUpRedirectorStatus = EStepStatus::Unknown; - EStepStatus SaveAllStatus = EStepStatus::Unknown; - - void OnLintReportEntered(); - void OnMarketplaceRecommendationsEntered(); - - bool LoadAssetsIfNeeded(const TArray& ObjectPaths, TArray& LoadedObjects); -}; diff --git a/Plugins/Linter/Source/Linter/Public/UI/SAssetLinkWidget.h b/Plugins/Linter/Source/Linter/Public/UI/SAssetLinkWidget.h deleted file mode 100644 index 6d5d821..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/SAssetLinkWidget.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#pragma once - -#include "EditorStyleSet.h" -#include "LinterStyle.h" -#include "Widgets/DeclarativeSyntaxSupport.h" -#include "Widgets/SCompoundWidget.h" -#include "Types/SlateStructs.h" -#include "Misc/ScopedSlowTask.h" - -class SAssetLinkWidget : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SAssetLinkWidget) - { - } - - SLATE_ATTRIBUTE(FAssetData, AssetData) - - SLATE_END_ARGS() - - TAttribute AssetData; - -public: - - void Construct(const FArguments& Args); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Public/UI/SStepWidget.h b/Plugins/Linter/Source/Linter/Public/UI/SStepWidget.h deleted file mode 100644 index a58560a..0000000 --- a/Plugins/Linter/Source/Linter/Public/UI/SStepWidget.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#pragma once - -#include "EditorStyleSet.h" -#include "LinterStyle.h" -#include "Widgets/DeclarativeSyntaxSupport.h" -#include "Widgets/SCompoundWidget.h" -#include "Types/SlateStructs.h" -#include "Misc/ScopedSlowTask.h" - - -enum EStepStatus { - NoStatus, - Unknown, - InProgress, - NeedsUpdate, - Warning, - Error, - Success -}; - -// Called when a step's action is invoked -DECLARE_DELEGATE_OneParam(FOnStepPerformAction, FScopedSlowTask&); - -/* Widget that represents a 'step' or 'choice' in the PaCK wizard. */ -class SStepWidget : public SCompoundWidget -{ -public: - - SLATE_BEGIN_ARGS(SStepWidget) - : _StepStatus(EStepStatus::NoStatus) - , _ShowStepStatusIcon(true) - { - } - - /** Name to display for this step. */ - SLATE_ATTRIBUTE(FText, StepName) - - /** Description to display for this step. */ - SLATE_ATTRIBUTE(FText, StepDesc) - - /** Text to display within the action button for this step. */ - SLATE_ATTRIBUTE(FText, StepActionText) - - /** Slate Brush to use as the thumbnail icon for this step. */ - SLATE_ATTRIBUTE(const FSlateBrush*, Icon) - - /** Current status for this step. */ - SLATE_ATTRIBUTE(EStepStatus, StepStatus) - - /** Current status for this step. */ - SLATE_ATTRIBUTE(bool, ShowStepStatusIcon) - - /** Delegate to fire when this step's action is invoked. */ - SLATE_EVENT(FOnStepPerformAction, OnPerformAction) - - SLATE_END_ARGS() - -public: - TAttribute StepStatus; - TAttribute StepActionText; - FOnStepPerformAction OnPerformAction; - TAttribute ShowStepStatusIcon; - - bool IsStepCompleted(bool bAllowWarning = true); - - void Construct(const FArguments& Args); -}; \ No newline at end of file diff --git a/Plugins/Linter/Source/MarketplaceLinter/MarketplaceLinter.Build.cs b/Plugins/Linter/Source/MarketplaceLinter/MarketplaceLinter.Build.cs deleted file mode 100644 index 55bc9f2..0000000 --- a/Plugins/Linter/Source/MarketplaceLinter/MarketplaceLinter.Build.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. - -using UnrealBuildTool; - -public class MarketplaceLinter : ModuleRules -{ - public MarketplaceLinter(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange( - new string[] - { - "Core", - "Linter" - } - ); - - - PrivateDependencyModuleNames.AddRange( - new string[] - { - "CoreUObject", - "Engine", - "Slate", - "SlateCore", - "RenderCore", - "UnrealEd", - "GraphEditor", - "AssetTools", - "EditorStyle", - "Projects", - "BlueprintGraph", - "InputCore", - "StandaloneRenderer", - "PropertyEditor", - "LevelEditor", - "LauncherPlatform", - "AppFramework", - "DesktopPlatform", - "UATHelper" - // ... add private dependencies that you statically link with here ... - } - ); - } -} diff --git a/Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp b/Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp deleted file mode 100644 index ba462fb..0000000 --- a/Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. - -#include "MarketplaceLinter.h" -#include "ISettingsModule.h" -#include "Framework/Docking/TabManager.h" -#include "LevelEditor.h" - -#include "Widgets/Input/SButton.h" -#include "Styling/SlateStyle.h" - -#define LOCTEXT_NAMESPACE "FMarketplaceLinterModule" - -void FMarketplaceLinterModule::StartupModule() -{ - -} - -void FMarketplaceLinterModule::ShutdownModule() -{ - -} - - -#undef LOCTEXT_NAMESPACE - -IMPLEMENT_MODULE(FMarketplaceLinterModule, MarketplaceLinter) -DEFINE_LOG_CATEGORY(LogMarketplaceLinter); \ No newline at end of file diff --git a/Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp b/Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp deleted file mode 100644 index 4d0fd9d..0000000 --- a/Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. -#include "MarketplaceNamingConvention.h" - - -UMarketplaceNamingConvention::UMarketplaceNamingConvention(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -#define SCRIPT_PATH(ScriptPath) TSoftClassPtr(FSoftObjectPath(TEXT("/Script/" #ScriptPath))) -#define ADD_PREFIX(ClassName, Prefix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix)); -#define ADD_PREFIX_SUFFIX(ClassName, Prefix, Suffix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix, Suffix)); - - // Animation - ADD_PREFIX(AimOffsetBlendSpace, "AO_"); - ADD_PREFIX(AimOffsetBlendSpace1D, "AO_"); - ADD_PREFIX(AnimBlueprint, "ABP_"); - ADD_PREFIX(AnimComposite, "AC_"); - ADD_PREFIX(AnimMontage, "AM_"); - ADD_PREFIX(AnimSequence, "A_"); - ADD_PREFIX(BlendSpace, "BS_"); - ADD_PREFIX(BlendSpace1D, "BS_"); - ADD_PREFIX(MorphTarget, "MT_"); - ADD_PREFIX(Rig, "Rig_"); - ADD_PREFIX(SkeletalMesh, "SK_"); - ADD_PREFIX(Skeleton, "SKEL_"); - - - // Artificial Intelligence - ADD_PREFIX(AIController, "AIC_"); - ADD_PREFIX(BehaviorTree, "BT_"); - ADD_PREFIX(BlackboardData, "BB_"); - ADD_PREFIX(BTDecorator, "BTDecorator_"); - ADD_PREFIX(BTService, "BTService_"); - ADD_PREFIX(BTTaskNode, "BTTask_"); - - // Blueprints - ADD_PREFIX(Blueprint, "BP_"); - ADD_PREFIX(BlueprintFunctionLibrary, "BPFL_"); - ADD_PREFIX(Interface, "BPI_"); - ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("IntroTutorials.EditorTutorial"), "TBP_")); - ADD_PREFIX(UserDefinedEnum, "E"); - ADD_PREFIX(UserDefinedStruct, "F"); - - // Materials - ADD_PREFIX(Material, "M_"); - ADD_PREFIX(Material, "MA_"); - ADD_PREFIX(Material, "MAT_"); - ADD_PREFIX(MaterialFunction, "MF_"); - ADD_PREFIX(MaterialInstance, "MI_"); - ADD_PREFIX(MaterialInstanceConstant, "MI_"); - ADD_PREFIX(MaterialParameterCollection, "MPC_"); - ADD_PREFIX(SubsurfaceProfile, "SP_"); - - // Textures - ADD_PREFIX(Texture2D, "T_"); - ADD_PREFIX(TextureCube, "TC_"); - ADD_PREFIX(TextureRenderTarget2D, "RT_"); - ADD_PREFIX(TextureRenderTargetCube, "RTC_"); - ADD_PREFIX(TextureLightProfile, "TLP_"); - - // Media - ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaTexture"), "MT_")); - ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaPlayer"), "MP_")); - - // Miscellaneous - ADD_PREFIX(VectorFieldAnimated, "VFA_"); - ADD_PREFIX(CameraAnim, "CA_"); - ADD_PREFIX(CurveLinearColor, "Curve_"); - ADD_PREFIX(CurveTable, "Curve_"); - ADD_PREFIX(DataTable, "DT_"); - ADD_PREFIX(CurveFloat, "Curve_"); - ADD_PREFIX(ForceFeedbackEffect, "FFE_"); - ADD_PREFIX(MatineeAnimInterface, "Matinee_"); - ADD_PREFIX(ObjectLibrary, "OL_"); - ADD_PREFIX(VectorFieldStatic, "VF_"); - ADD_PREFIX(TouchInterface, "TI_"); - ADD_PREFIX(CurveVector, "Curve_"); - ADD_PREFIX(StaticMesh, "SM_"); - ADD_PREFIX(StaticMesh, "S_"); - - // Paper 2D - - // Physics - ADD_PREFIX(PhysicalMaterial, "PM_"); - ADD_PREFIX(PhysicsAsset, "PHYS_"); - - // Sounds - ADD_PREFIX(DialogueVoice, "DV_"); - ADD_PREFIX(DialogueWave, "DW_"); - ADD_PREFIX(ReverbEffect, "Reverb_"); - ADD_PREFIX(SoundAttenuation, "ATT_"); - ADD_PREFIX(SoundClass, ""); - ADD_PREFIX(SoundConcurrency, "_SC"); - ADD_PREFIX_SUFFIX(SoundCue, "A_", "_Cue"); - ADD_PREFIX(SoundMix, "Mix_"); - ADD_PREFIX(SoundWave, "A_"); - - // User Interface - ADD_PREFIX(Font, "Font_"); - ADD_PREFIX(SlateBrushAsset, "Brush_"); - ADD_PREFIX(SlateWidgetStyleAsset, "Style_"); - ADD_PREFIX(WidgetBlueprint, "WBP_"); - - // Effects - ADD_PREFIX(ParticleSystem, "PS_"); - -#undef ADD_PREFIX - - SortConventions(); -} - diff --git a/Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceLinter.h b/Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceLinter.h deleted file mode 100644 index a47dd34..0000000 --- a/Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceLinter.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. - -#pragma once - -#include "Modules/ModuleManager.h" - -DECLARE_LOG_CATEGORY_EXTERN(LogMarketplaceLinter, Verbose, All); - -class MARKETPLACELINTER_API FMarketplaceLinterModule : public IModuleInterface -{ -public: - - /** IModuleInterface implementation */ - virtual void StartupModule() override; - virtual void ShutdownModule() override; - -}; \ No newline at end of file diff --git a/README.md b/README.md index 7c5f974..cad1989 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,59 @@ # Linter -Currently WIP release of Linter. +Linter for Unreal Blueprints. Based of the [Linter v2](https://ue5.style), made compatible with Unreal Engine 5. +The original version can still be found on the [Marketplace](https://www.unrealengine.com/marketplace/product/linter-v2) or the original [GitHub Repository](https://github.com/ue4plugins/Linter). -@TODO: +## Usage +The Linter can be used in two ways: -* 4.27 patchups per community -* Compilation and release guide \ No newline at end of file +### Usage in Editor +The Linter can be used to lint Assets in the Editor. +To do so, select the Assets you want to lint. These can be single Assets or multiple Assets/Folders. +When right-clicking on a selected Asset, you can find the **Linter** option at the bottom the context menu: + +### Usage as Commandlet +The Linter can also be used as a Commandlet and executed from the command line: +`UnrealEditor-Cmd.exe -run=Linter ` +If no Path is provided, the Linter will default to `/Game` and lint the whole Content Directory. + +Advanced Options: +- `-RuleSet=` Specify a custom LintRuleSet to use (see below). Default: **Marketplace** +- `-json and -json=` Write the Linter output to a JSON file. Default: **\/Saved/LintReports/** +- `-html and -html=` Write the Linter output to a interactive HTML file. Default: **\/Saved/LintReports/** +- Both `-json` and `-html` can be absolute or relative paths and can contain a filename for the file. Both Options can be used at the same time. + +### Understanding RuleSets +The Linter works with a Collection of LintRules, which are grouped into RuleSets. +Each LintRule can check for a specific quality or style issue in a Blueprint. +The Linter provides 2 RuleSets by default: +- `Marketplace` checks against the [Epic Games Marketplace Guidelines](https://www.unrealengine.com/marketplace-guidelines) +- `ue4.style` checks against the [ue4.style](http://ue4.style/) guidelines. + +You can define your own RuleSets by creating a new DataAsset of type `LintRuleSet` and adding LintRules to it. +To create new LintRules, subclass `ULintRule` and override the `PassesRule` function. +When using the Linter as a Commandlet, you can specify a RuleSet, predefined or your own, with the `-RuleSet` option. + +## Installation + +### Into a Project +To install the Linter into a Project, simply clone the `Linter` folder into the `Plugins` folder of your Project. + +### Into the Engine +To install the Linter into the Engine, clone the `Linter` into some Place on your Computer. +You then need to Build the Plugin using the UAT and copy the built Plugin into the `Engine/Plugins/Marketplace/` folder. + +```bash +# Clone Repository +git clone https://github.com/jwindgassen/Linter.git Linter + +# Build Plugin. Both Paths must be absolute, otherwise UAT will complain. +# You cannot Build the Plugin directly into the Engine folder! +/Engine/Build/BatchFiles/RunUAT.bat BuildPlugin -Plugin=`pwd`/Linter/Linter.uplugin -Package=`pwd`/Linter_ + +# Copy Plugin into Engine and Clean up +cp -r Linter_ /Engine/Plugins/Marketplace/Linter +rm -r Linter Linter_ +``` + +If you have [ue4cli](https://github.com/adamrehn/ue4cli) installed (which I highly recommend), you can simply use `ue4 uat` +instead of `/Engine/Build/BatchFiles/RunUAT.bat` diff --git a/Plugins/Linter/Resources/Icon128.png b/Resources/Icon128.png similarity index 100% rename from Plugins/Linter/Resources/Icon128.png rename to Resources/Icon128.png diff --git a/Resources/LintReportTemplate.html b/Resources/LintReportTemplate.html new file mode 100644 index 0000000..8c991b1 --- /dev/null +++ b/Resources/LintReportTemplate.html @@ -0,0 +1,363 @@ + + + + + + + Lint Report + + + + + + + + + + +
+

Lint Results for 

+

+
+ +
+
+ + + + + + + + + + + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/GamemakinLinter/GamemakinLinter.Build.cs b/Source/GamemakinLinter/GamemakinLinter.Build.cs new file mode 100644 index 0000000..54d1ec7 --- /dev/null +++ b/Source/GamemakinLinter/GamemakinLinter.Build.cs @@ -0,0 +1,11 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +using UnrealBuildTool; + +public class GamemakinLinter : ModuleRules { + public GamemakinLinter(ReadOnlyTargetRules Target) : base(Target) { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new[] { "Core", "CoreUObject", "Engine", "Linter" }); + } +} \ No newline at end of file diff --git a/Source/GamemakinLinter/Private/GamemakinLinter.cpp b/Source/GamemakinLinter/Private/GamemakinLinter.cpp new file mode 100644 index 0000000..70d645b --- /dev/null +++ b/Source/GamemakinLinter/Private/GamemakinLinter.cpp @@ -0,0 +1,7 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "GamemakinLinter.h" + + +IMPLEMENT_MODULE(FGamemakinLinterModule, GamemakinLinter) +DEFINE_LOG_CATEGORY(LogGamemakinLinter); diff --git a/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp b/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp new file mode 100644 index 0000000..7ffad8a --- /dev/null +++ b/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp @@ -0,0 +1,108 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "GamemakinNamingConvention.h" + + +UGamemakinNamingConvention::UGamemakinNamingConvention(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { +#define SCRIPT_PATH(ScriptPath) TSoftClassPtr(FSoftObjectPath(TEXT("/Script/" #ScriptPath))) +#define ADD_PREFIX(ClassName, Prefix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix)); +#define ADD_PREFIX_SUFFIX(ClassName, Prefix, Suffix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix, Suffix)); + + // Animation + ADD_PREFIX(AimOffsetBlendSpace, "AO_"); + ADD_PREFIX(AimOffsetBlendSpace1D, "AO_"); + ADD_PREFIX(AnimBlueprint, "ABP_"); + ADD_PREFIX(AnimComposite, "AC_"); + ADD_PREFIX(AnimMontage, "AM_"); + ADD_PREFIX(AnimSequence, "A_"); + ADD_PREFIX(BlendSpace, "BS_"); + ADD_PREFIX(BlendSpace1D, "BS_"); + ADD_PREFIX(MorphTarget, "MT_"); + ADD_PREFIX(Rig, "Rig_"); + ADD_PREFIX(SkeletalMesh, "SK_"); + ADD_PREFIX(Skeleton, "SKEL_"); + + + // Artificial Intelligence + ADD_PREFIX(AIController, "AIC_"); + ADD_PREFIX(BehaviorTree, "BT_"); + ADD_PREFIX(BlackboardData, "BB_"); + ADD_PREFIX(BTDecorator, "BTDecorator_"); + ADD_PREFIX(BTService, "BTService_"); + ADD_PREFIX(BTTaskNode, "BTTask_"); + + // Blueprints + ADD_PREFIX(Blueprint, "BP_"); + ADD_PREFIX(BlueprintFunctionLibrary, "BPFL_"); + ADD_PREFIX(Interface, "BPI_"); + ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("IntroTutorials.EditorTutorial"), "TBP_")); + ADD_PREFIX(UserDefinedEnum, "E"); + ADD_PREFIX(UserDefinedStruct, "F"); + + // Materials + ADD_PREFIX(Material, "M_"); + ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.Material"))), "PP_", TEXT(""), "PostProcess")); + ADD_PREFIX(MaterialFunction, "MF_"); + ADD_PREFIX(MaterialInstance, "MI_"); + ADD_PREFIX(MaterialInstanceConstant, "MI_"); + ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.MaterialInstance"))), "PPI_", TEXT(""), "PostProcess")); + ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.MaterialInstanceConstant"))), "PPI_", TEXT(""), "PostProcess")); + ADD_PREFIX(MaterialParameterCollection, "MPC_"); + ADD_PREFIX(SubsurfaceProfile, "SP_"); + + // Textures + ADD_PREFIX(Texture2D, "T_"); + ADD_PREFIX(TextureCube, "TC_"); + ADD_PREFIX(TextureRenderTarget2D, "RT_"); + ADD_PREFIX(TextureRenderTargetCube, "RTC_"); + ADD_PREFIX(TextureLightProfile, "TLP_"); + + // Media + ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaTexture"), "MT_")); + ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaPlayer"), "MP_")); + + // Miscellaneous + ADD_PREFIX(VectorFieldAnimated, "VFA_"); + ADD_PREFIX(CameraAnim, "CA_"); + ADD_PREFIX(CurveLinearColor, "Curve_"); + ADD_PREFIX(CurveTable, "Curve_"); + ADD_PREFIX(DataTable, "DT_"); + ADD_PREFIX(CurveFloat, "Curve_"); + ADD_PREFIX(ForceFeedbackEffect, "FFE_"); + ADD_PREFIX(MatineeAnimInterface, "Matinee_"); + ADD_PREFIX(ObjectLibrary, "OL_"); + ADD_PREFIX(VectorFieldStatic, "VF_"); + ADD_PREFIX(TouchInterface, "TI_"); + ADD_PREFIX(CurveVector, "Curve_"); + ADD_PREFIX(StaticMesh, "S_"); + + // Paper 2D + + // Physics + ADD_PREFIX(PhysicalMaterial, "PM_"); + ADD_PREFIX(PhysicsAsset, "PHYS_"); + + // Sounds + ADD_PREFIX(DialogueVoice, "DV_"); + ADD_PREFIX(DialogueWave, "DW_"); + ADD_PREFIX(ReverbEffect, "Reverb_"); + ADD_PREFIX(SoundAttenuation, "ATT_"); + ADD_PREFIX(SoundClass, ""); + ADD_PREFIX(SoundConcurrency, "_SC"); + ADD_PREFIX_SUFFIX(SoundCue, "A_", "_Cue"); + ADD_PREFIX(SoundMix, "Mix_"); + ADD_PREFIX(SoundWave, "A_"); + + // User Interface + ADD_PREFIX(Font, "Font_"); + ADD_PREFIX(SlateBrushAsset, "Brush_"); + ADD_PREFIX(SlateWidgetStyleAsset, "Style_"); + ADD_PREFIX(WidgetBlueprint, "WBP_"); + + // Effects + ADD_PREFIX(ParticleSystem, "PS_"); + +#undef ADD_PREFIX + + SortConventions(); +} diff --git a/Source/GamemakinLinter/Public/GamemakinLinter.h b/Source/GamemakinLinter/Public/GamemakinLinter.h new file mode 100644 index 0000000..7717a81 --- /dev/null +++ b/Source/GamemakinLinter/Public/GamemakinLinter.h @@ -0,0 +1,11 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + +DECLARE_LOG_CATEGORY_EXTERN(LogGamemakinLinter, Verbose, All); + +class GAMEMAKINLINTER_API FGamemakinLinterModule : public IModuleInterface { +public: + virtual bool SupportsDynamicReloading() override { + return false; + } +}; diff --git a/Plugins/Linter/Source/GamemakinLinter/Public/GamemakinNamingConvention.h b/Source/GamemakinLinter/Public/GamemakinNamingConvention.h similarity index 66% rename from Plugins/Linter/Source/GamemakinLinter/Public/GamemakinNamingConvention.h rename to Source/GamemakinLinter/Public/GamemakinNamingConvention.h index 38f9aa9..19a3893 100644 --- a/Plugins/Linter/Source/GamemakinLinter/Public/GamemakinNamingConvention.h +++ b/Source/GamemakinLinter/Public/GamemakinNamingConvention.h @@ -1,17 +1,14 @@ // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. #pragma once -#include "CoreMinimal.h" + #include "LinterNamingConvention.h" #include "GamemakinNamingConvention.generated.h" UCLASS() -class UGamemakinNamingConvention : public ULinterNamingConvention -{ - GENERATED_BODY() +class UGamemakinNamingConvention : public ULinterNamingConvention { + GENERATED_BODY() public: - - UGamemakinNamingConvention(const FObjectInitializer& ObjectInitializer); - + UGamemakinNamingConvention(const FObjectInitializer& ObjectInitializer); }; diff --git a/Source/Linter/Linter.Build.cs b/Source/Linter/Linter.Build.cs new file mode 100644 index 0000000..4e07e6d --- /dev/null +++ b/Source/Linter/Linter.Build.cs @@ -0,0 +1,22 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +using UnrealBuildTool; + +public class Linter : ModuleRules { + public Linter(ReadOnlyTargetRules Target) : base(Target) { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new[] { "Core" }); + PrivateDependencyModuleNames.AddRange(new[] { + "CoreUObject", "Engine", "Slate", "SlateCore", "AppFramework", + "InputCore", "UnrealEd", "GraphEditor", "AssetTools", "EditorStyle", "BlueprintGraph", "PropertyEditor", + "LauncherPlatform", "Projects", "DesktopPlatform", "Json", "UATHelper", "ToolMenus", "ContentBrowser", "ContentBrowserData" + }); + + PublicIncludePathModuleNames.Add("Launch"); + +#if UE_4_20_OR_LATER + PublicDefinitions.Add("UE_4_20_OR_LATER=1"); +#endif + } +} \ No newline at end of file diff --git a/Plugins/Linter/Source/Linter/Private/AnyObject_LinterDummyClass.cpp b/Source/Linter/Private/AnyObject_LinterDummyClass.cpp similarity index 59% rename from Plugins/Linter/Source/Linter/Private/AnyObject_LinterDummyClass.cpp rename to Source/Linter/Private/AnyObject_LinterDummyClass.cpp index e1b68d3..248cc0b 100644 --- a/Plugins/Linter/Source/Linter/Private/AnyObject_LinterDummyClass.cpp +++ b/Source/Linter/Private/AnyObject_LinterDummyClass.cpp @@ -1,7 +1,4 @@ #include "AnyObject_LinterDummyClass.h" -UAnyObject_LinterDummyClass::UAnyObject_LinterDummyClass(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ - -} +UAnyObject_LinterDummyClass::UAnyObject_LinterDummyClass(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} diff --git a/Source/Linter/Private/BatchRenameTool/BatchRenameTool.cpp b/Source/Linter/Private/BatchRenameTool/BatchRenameTool.cpp new file mode 100644 index 0000000..9c82a51 --- /dev/null +++ b/Source/Linter/Private/BatchRenameTool/BatchRenameTool.cpp @@ -0,0 +1,318 @@ +// Copyright 2016 Gamemakin LLC. All Rights Reserved. + +#include "BatchRenameTool/BatchRenameTool.h" + +#include "AssetToolsModule.h" +#include "Editor.h" +#include "IAssetTools.h" +#include "Framework/Application/SlateApplication.h" +#include "Framework/Notifications/NotificationManager.h" +#include "Framework/Text/TextLayout.h" +#include "Logging/MessageLog.h" +#include "Logging/TokenizedMessage.h" +#include "Misc/EngineVersionComparison.h" +#include "Modules/ModuleManager.h" +#include "Types/SlateEnums.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Layout/SSeparator.h" +#include "Widgets/Layout/SUniformGridPanel.h" +#include "Widgets/Notifications/SNotificationList.h" +#include "Widgets/Text/STextBlock.h" + +#define LOCTEXT_NAMESPACE "LinterBatchRenamer" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +FDlgBatchRenameTool::FDlgBatchRenameTool(const TArray Assets) : + bRemovePrefix(false), + bRemoveSuffix(false), + SelectedAssets(Assets) { + if (FSlateApplication::IsInitialized()) { + // clang-format off + // @formatter:off + DialogWindow = SNew(SWindow) + .Title(LOCTEXT("BatchRenameToolDlgTitle", "Batch Rename Tool")) + .SupportsMinimize(false).SupportsMaximize(false) + .SaneWindowPlacement(true) + .AutoCenter(EAutoCenter::PreferredWorkArea) + .ClientSize(FVector2D(350, 165)); + + const TSharedPtr DialogWrapper = + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(4.0f) + [ + SAssignNew(DialogWidget, SDlgBatchRenameTool) + .ParentWindow(DialogWindow) + ]; + // clang-format on + // @formatter:on + + DialogWindow->SetContent(DialogWrapper.ToSharedRef()); + } +} + +FDlgBatchRenameTool::EResult FDlgBatchRenameTool::ShowModal() { + //Show Dialog + GEditor->EditorAddModalWindow(DialogWindow.ToSharedRef()); + const EResult UserResponse = DialogWidget->GetUserResponse(); + + if (UserResponse == Confirm) { + Prefix = DialogWidget->PrefixTextBox->GetText().ToString(); + Suffix = DialogWidget->SuffixTextBox->GetText().ToString(); + bRemovePrefix = DialogWidget->PrefixRemoveBox->IsChecked(); + bRemoveSuffix = DialogWidget->SuffixRemoveBox->IsChecked(); + + Find = DialogWidget->FindTextBox->GetText().ToString(); + Replace = DialogWidget->ReplaceTextBox->GetText().ToString(); + + // If no information is given, treat as canceled + if (Prefix.IsEmpty() && Suffix.IsEmpty() && Find.IsEmpty()) { + return Cancel; + } + + const FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); + TArray AssetsAndNames; + + for (auto AssetIt = SelectedAssets.CreateConstIterator(); AssetIt; ++AssetIt) { + const FAssetData& Asset = *AssetIt; + + // Early out on assets that can not be renamed +#if UE_VERSION_NEWER_THAN(5, 1, 0) + if (!(!Asset.IsRedirector() && Asset.AssetClassPath.GetAssetName() != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))) +#else + if (!(!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly))) +#endif + { + continue; + } + + // Work on a copy of the asset name and see if after name operations + // if the copy is different than the original before creating rename data + FString AssetNewName = Asset.AssetName.ToString(); + + if (!Find.IsEmpty()) { + AssetNewName.ReplaceInline(*Find, *Replace); + } + + if (!Prefix.IsEmpty()) { + if (bRemovePrefix) { + AssetNewName.RemoveFromStart(Prefix, ESearchCase::CaseSensitive); + } else { + if (!AssetNewName.StartsWith(Prefix, ESearchCase::CaseSensitive)) { + AssetNewName.InsertAt(0, Prefix); + } + } + } + + if (!Suffix.IsEmpty()) { + if (bRemoveSuffix) { + AssetNewName.RemoveFromEnd(Suffix, ESearchCase::CaseSensitive); + } else { + if (!AssetNewName.EndsWith(Suffix, ESearchCase::CaseSensitive)) { + AssetNewName = AssetNewName.Append(Suffix); + } + } + } + + if (AssetNewName != Asset.AssetName.ToString()) { + AssetsAndNames.Push(FAssetRenameData(Asset.GetAsset(), Asset.PackagePath.ToString(), AssetNewName)); + } + } + + if (!AssetToolsModule.Get().RenameAssets(AssetsAndNames)) { + FNotificationInfo NotificationInfo(LOCTEXT("BatchRenameFailed", "Batch Rename operation did not fully complete successfully. Maybe fix up redirectors? Check Output Log for details!")); + NotificationInfo.ExpireDuration = 6.0f; + NotificationInfo.Hyperlink = FSimpleDelegate::CreateStatic([]() { + FMessageLog("LoadErrors").Open(EMessageSeverity::Info, true); + }); + NotificationInfo.HyperlinkText = LOCTEXT("LoadObjectHyperlink", "Show Message Log"); + FSlateNotificationManager::Get().AddNotification(NotificationInfo); + } + } + return UserResponse; +} + +void SDlgBatchRenameTool::Construct(const FArguments& InArgs) { + UserResponse = FDlgBatchRenameTool::Cancel; + ParentWindow = InArgs._ParentWindow.Get(); + + this->ChildSlot[ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .HAlign(HAlign_Right) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SNew(SBox) + .WidthOverride(48.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("BatchRenameToolDlgPrefix", "Prefix")) + .Justification(ETextJustify::Right) + ] + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SAssignNew(PrefixTextBox, SEditableTextBox) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.0f, 0.0f, 0.0f, 0.0f) + [ + SAssignNew(PrefixRemoveBox, SCheckBox) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("BatchRenameToolDlgRemove", "Remove")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .HAlign(HAlign_Right) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SNew(SBox) + .WidthOverride(48.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("BatchRenameToolDlgSuffix", "Suffix")) + .Justification(ETextJustify::Right) + ] + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SAssignNew(SuffixTextBox, SEditableTextBox) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.0f, 0.0f, 0.0f, 0.0f) + [ + SAssignNew(SuffixRemoveBox, SCheckBox) + ] + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("BatchRenameToolDlgRemove", "Remove")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SSeparator) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SNew(SBox) + .WidthOverride(48.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("BatchRenameToolDlgFind", "Find")) + .Justification(ETextJustify::Right) + ] + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SAssignNew(FindTextBox, SEditableTextBox) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SNew(SBox) + .WidthOverride(48.0f) + [ + SNew(STextBlock) + .Text(LOCTEXT("BatchRenameToolDlgReplace", "Replace")) + .Justification(ETextJustify::Right) + ] + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + .Padding(0.0f, 0.0f, 8.0f, 0.0f) + [ + SAssignNew(ReplaceTextBox, SEditableTextBox) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SSeparator) + ] + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Right) + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SUniformGridPanel) + .SlotPadding(FAppStyle::GetMargin("StandardDialog.SlotPadding")) + .MinDesiredSlotWidth(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotWidth")) + .MinDesiredSlotHeight(FAppStyle::GetFloat("StandardDialog.MinDesiredSlotHeight")) + + SUniformGridPanel::Slot(0, 0) + [ + SNew(SButton) + .HAlign(HAlign_Center) + .ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(this, &SDlgBatchRenameTool::OnButtonClick, FDlgBatchRenameTool::Confirm) + .Text(LOCTEXT("SkeletonMergeOk", "OK")) + ] + + SUniformGridPanel::Slot(1, 0) + [ + SNew(SButton) + .HAlign(HAlign_Center) + .ContentPadding(FAppStyle::GetMargin("StandardDialog.ContentPadding")) + .OnClicked(this, &SDlgBatchRenameTool::OnButtonClick, FDlgBatchRenameTool::Cancel) + .Text(LOCTEXT("SkeletonMergeCancel", "Cancel")) + ] + ] + ]; +} + +FDlgBatchRenameTool::EResult SDlgBatchRenameTool::GetUserResponse() const { + return UserResponse; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/LintRule.cpp b/Source/Linter/Private/LintRule.cpp new file mode 100644 index 0000000..b8a6b54 --- /dev/null +++ b/Source/Linter/Private/LintRule.cpp @@ -0,0 +1,210 @@ +#include "LintRule.h" + +#include "Runtime/Launch/Resources/Version.h" +#include "Materials/MaterialInterface.h" +#include "Materials/Material.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "Engine/Blueprint.h" +#include "Modules/ModuleManager.h" +#include "IAssetTools.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Misc/EngineVersionComparison.h" +#if UE_VERSION_NEWER_THAN(5, 2, 0) +#include "MaterialDomain.h" +#endif + + +ULintRule::ULintRule(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, OUT TArray& OutRuleViolations) const { + return true; +} + +bool ULintRule::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + OutRuleViolations.Empty(); + + if (ObjectToLint == nullptr) { + return true; + } + + if (ParentRuleSet == nullptr) { + return true; + } + + if (IsRuleSuppressed()) { + return true; + } + + return PassesRule_Internal(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule::IsRuleSuppressed() const { + return false; +} + +FName ULintRule::GetRuleBasedObjectVariantName_Implementation(UObject* ObjectToLint) const { + if (ObjectToLint == nullptr) { + return NAME_None; + } + + { + const UMaterialInterface* MI = Cast(ObjectToLint); + if (MI != nullptr) { +#if UE_VERSION_NEWER_THAN(4, 25, 0) + TMicRecursionGuard RecursionGuard; +#else + UMaterialInterface::TMicRecursionGuard RecursionGuard; +#endif + const UMaterial* Material = MI->GetMaterial_Concurrent(RecursionGuard); + if (Material != nullptr) { + if (Material->MaterialDomain == MD_PostProcess) { + return "PostProcess"; + } + } + } + } + + { + const UBlueprint* Blueprint = Cast(ObjectToLint); + if (Blueprint != nullptr) { + if (Blueprint->BlueprintType == BPTYPE_MacroLibrary) { + return "MacroLibrary"; + } + + if (FBlueprintEditorUtils::IsInterfaceBlueprint(Blueprint)) { + return "Interface"; + } + + if (Blueprint->BlueprintType == BPTYPE_FunctionLibrary) { + return "FunctionLibrary"; + } + } + } + + return NAME_None; +} + +TArray FLintRuleViolation::AllRuleViolationsWithViolator(const TArray& RuleViolationCollection, const UObject* SearchViolator) { + return RuleViolationCollection.FilterByPredicate([SearchViolator](const FLintRuleViolation& RuleViolation) { + if (SearchViolator != nullptr && RuleViolation.Violator.Get() == SearchViolator) { + return true; + } + + return false; + }); +} + +TArray> FLintRuleViolation::AllRuleViolationsWithViolatorShared(const TArray>& RuleViolationCollection, const UObject* SearchViolator) { + return RuleViolationCollection.FilterByPredicate([SearchViolator](const TSharedPtr& RuleViolation) { + if (SearchViolator != nullptr && RuleViolation->Violator.Get() == SearchViolator) { + return true; + } + + return false; + }); +} + +TArray> FLintRuleViolation::AllRuleViolationsWithViolatorShared(const TArray& RuleViolationCollection, const UObject* SearchViolator) { + // This should really be done when the structs are first created + TArray> SharedViolations; + TArray Violations = AllRuleViolationsWithViolator(RuleViolationCollection, SearchViolator); + for (const FLintRuleViolation& Violation : Violations) { + SharedViolations.Push(MakeShared(Violation)); + } + + return SharedViolations; +} + +TArray FLintRuleViolation::AllRuleViolationsOfSpecificRule(const TArray& RuleViolationCollection, TSubclassOf SearchRule) { + return RuleViolationCollection.FilterByPredicate([SearchRule](const FLintRuleViolation& RuleViolation) { + if (SearchRule.Get() != nullptr && RuleViolation.ViolatedRule == SearchRule) { + return true; + } + + return false; + }); +} + +TArray FLintRuleViolation::AllRuleViolationsOfRuleGroup(const TArray& RuleViolationCollection, FName SearchRuleGroup) { + return RuleViolationCollection.FilterByPredicate([SearchRuleGroup](const FLintRuleViolation& RuleViolation) { + if (RuleViolation.ViolatedRule.Get() != nullptr && RuleViolation.ViolatedRule.GetDefaultObject()->RuleGroup == SearchRuleGroup) { + return true; + } + + return false; + }); +} + +TArray FLintRuleViolation::AllRuleViolationViolators(const TArray& RuleViolationCollection) { + TArray Violators; + for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) { + Violators.AddUnique(RuleViolation.Violator.Get()); + } + return Violators; +} + +TArray FLintRuleViolation::AllRuleViolationViolators(const TArray>& RuleViolationCollection) { + TArray Violators; + for (const TSharedPtr& RuleViolation : RuleViolationCollection) { + Violators.AddUnique(RuleViolation->Violator.Get()); + } + return Violators; +} + +TMultiMap FLintRuleViolation::AllRuleViolationsMappedByViolator(const TArray& RuleViolationCollection) { + TMultiMap ViolatorViolationsMultiMap; + + for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) { + ViolatorViolationsMultiMap.Add(RuleViolation.Violator.Get(), RuleViolation); + } + + return ViolatorViolationsMultiMap; +} + +TMultiMap FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRule(const TArray& RuleViolationCollection) { + TMultiMap LintRuleViolationsMultiMap; + + for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) { + LintRuleViolationsMultiMap.Add(RuleViolation.ViolatedRule.GetDefaultObject(), RuleViolation); + } + + return LintRuleViolationsMultiMap; +} + +TMultiMap> FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRuleShared(const TArray& RuleViolationCollection) { + // We should really just create shared ptrs to begin with instead of doing this + TMultiMap> LintRuleViolationsMultiMap; + + for (const FLintRuleViolation& RuleViolation : RuleViolationCollection) { + LintRuleViolationsMultiMap.Add(RuleViolation.ViolatedRule.GetDefaultObject(), MakeShared(RuleViolation)); + } + + return LintRuleViolationsMultiMap; +} + +TMultiMap> FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRuleShared(const TArray>& RuleViolationCollection) { + TMultiMap> LintRuleViolationsMultiMap; + + for (const TSharedPtr& RuleViolation : RuleViolationCollection) { + LintRuleViolationsMultiMap.Add(RuleViolation->ViolatedRule.GetDefaultObject(), RuleViolation); + } + + return LintRuleViolationsMultiMap; +} + +bool FLintRuleViolation::PopulateAssetData() { + const IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked("AssetRegistry").Get(); + TArray AssetRenameData; + + if (Violator.IsValid()) { +#if UE_VERSION_NEWER_THAN(5, 1, 0) + ViolatorAssetData = AssetRegistry.GetAssetByObjectPath(Violator->GetPathName()); +#else + ViolatorAssetData = AssetRegistry.GetAssetByObjectPath(FName(*Violator->GetPathName())); +#endif + return ViolatorAssetData.IsValid(); + } + + return false; +} diff --git a/Source/Linter/Private/LintRuleSet.cpp b/Source/Linter/Private/LintRuleSet.cpp new file mode 100644 index 0000000..2dcec85 --- /dev/null +++ b/Source/Linter/Private/LintRuleSet.cpp @@ -0,0 +1,269 @@ +#include "LintRuleSet.h" + +#include "AnyObject_LinterDummyClass.h" +#include "Interfaces/IPluginManager.h" +#include "LintRunner.h" +#include "Linter.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Modules/ModuleManager.h" +#include "HAL/RunnableThread.h" + + +TArray> ULintResults::GetSharedViolations() const { + TArray> SharedRuleViolations; + for (const FLintRuleViolation& Violation : Violations) { + TSharedPtr SharedViolation = MakeShared(Violation); + SharedViolation->PopulateAssetData(); + SharedRuleViolations.Push(SharedViolation); + } + + return SharedRuleViolations; +} + +TSharedPtr ULintResults::GenerateJsonReport() const { + auto Report = MakeShared(); + + Report->SetStringField("Project", FPaths::GetBaseFilename(FPaths::GetProjectFilePath())); + Report->SetStringField("LintRuleSet", LintRuleSet); + Report->SetStringField("Result", Result.ToString()); + Report->SetNumberField("Warnings", Warnings); + Report->SetNumberField("Errors", Errors); + + TArray> PathArray; + for (const auto& Path : Paths) { + PathArray.Add(MakeShared(Path)); + } + Report->SetArrayField("Paths", PathArray); + + TArray> AssetsArray; + for (const auto& Asset : CheckedAssets) { +#if UE_VERSION_NEWER_THAN(5, 1, 0) + AssetsArray.Add(MakeShared(Asset.GetObjectPathString())); +#else + AssetsArray.Add(MakeShared(Asset.ObjectPath.ToString())); +#endif + } + Report->SetArrayField("CheckedAssets", AssetsArray); + + TArray> ViolationsArray; + for (const UObject* Violator : FLintRuleViolation::AllRuleViolationViolators(Violations)) { + TSharedPtr ViolationObject = MakeShareable(new FJsonObject); + + FAssetData AssetData; + TArray ViolatorViolations = FLintRuleViolation::AllRuleViolationsWithViolator(Violations, Violator); + + if (ViolatorViolations.Num() > 0) { + ViolatorViolations[0].PopulateAssetData(); + AssetData = ViolatorViolations[0].ViolatorAssetData; + + ViolationObject->SetStringField("AssetName", AssetData.AssetName.ToString()); + ViolationObject->SetStringField("AssetFullName", AssetData.GetFullName()); +#if UE_VERSION_NEWER_THAN(5, 1, 0) + ViolationObject->SetStringField("AssetPath", AssetData.GetObjectPathString()); +#else + ViolationObject->SetStringField("ViolatorAssetPath", AssetData.ObjectPath.ToString()); +#endif + //@TODO: Thumbnail export? + + TArray> ViolationObjects; + for (const FLintRuleViolation& Violation : ViolatorViolations) { + ULintRule* LintRule = Violation.ViolatedRule->GetDefaultObject(); + check(LintRule != nullptr); + + TSharedPtr RuleJsonObject = MakeShareable(new FJsonObject); + RuleJsonObject->SetStringField("Group", LintRule->RuleGroup.ToString()); + RuleJsonObject->SetStringField("Title", LintRule->RuleTitle.ToString()); + RuleJsonObject->SetStringField("Description", LintRule->RuleDescription.ToString()); + RuleJsonObject->SetStringField("RuleURL", LintRule->RuleURL); + RuleJsonObject->SetNumberField("Severity", static_cast(LintRule->RuleSeverity)); + RuleJsonObject->SetStringField("RecommendedAction", Violation.RecommendedAction.ToString()); + + ViolationObjects.Add(MakeShared(RuleJsonObject)); + } + + ViolationObject->SetArrayField("Violations", ViolationObjects); + } + + ViolationsArray.Add(MakeShared(ViolationObject)); + } + Report->SetArrayField("Violators", ViolationsArray); + + return Report; +} + +FString ULintResults::GenerateJsonReportString() const { + const TSharedPtr Report = GenerateJsonReport(); + + FString ReportString; + const TSharedRef>> Writer = TJsonWriterFactory>::Create(&ReportString); + FJsonSerializer::Serialize(Report.ToSharedRef(), Writer); + + return ReportString; +} + +FString ULintResults::GenerateHTML() const { + const FString ReportString = GenerateJsonReportString(); + + static const FString TemplatePath = IPluginManager::Get().FindPlugin("Linter")->GetBaseDir() / "Resources" /"LintReportTemplate.html"; + UE_LOG(LogLinter, Display, TEXT("Loading HTML report template from %s"), *TemplatePath); + + FString Template; + if (!FFileHelper::LoadFileToString(Template, *TemplatePath)) { + UE_LOG(LogLinter, Error, TEXT("Could not load HTML report template.")); + } + + Template.ReplaceInline(TEXT("{% Report %}"), *ReportString); + return Template; +} + +ULinterNamingConvention* ULintRuleSet::GetNamingConvention() const { + return NamingConvention.Get(); +} + +ULintResults* ULintRuleSet::LintPath(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask /*= nullptr*/) const { + // ReSharper disable once CppExpressionWithoutSideEffects + NamingConvention.LoadSynchronous(); + + ULintResults* Results = NewObject(); + Results->LintRuleSet = NameForCommandlet; + + if (AssetPaths.Num() == 0) { + AssetPaths.Push(TEXT("/Game")); + } + Results->Paths = AssetPaths; + + // Begin loading assets + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + + UE_LOG(LogLinter, Display, TEXT("Loading the asset registry...")); + AssetRegistryModule.Get().SearchAllAssets(/*bSynchronousSearch =*/true); + UE_LOG(LogLinter, Display, TEXT("Finished loading the asset registry. Loading assets...")); + + FARFilter ARFilter; + ARFilter.bRecursivePaths = true; + + for (const FString& AssetPath : AssetPaths) { + UE_LOG(LogLinter, Display, TEXT("Adding path \"%s\" to be linted."), *AssetPath); + ARFilter.PackagePaths.Push(FName(*AssetPath)); + } + + AssetRegistryModule.Get().GetAssets(ARFilter, Results->CheckedAssets); + + TArray LintRunners; + TArray Threads; + + if (ParentScopedSlowTask != nullptr) { + ParentScopedSlowTask->TotalAmountOfWork = Results->CheckedAssets.Num() + 2; + ParentScopedSlowTask->CompletedWork = 0.0f; + } + + for (const FAssetData& Asset : Results->CheckedAssets) { + check(Asset.IsValid()); + UE_LOG(LogLinter, Verbose, TEXT("Creating Lint Thread for asset \"%s\"."), *Asset.AssetName.ToString()); + UObject* Object = Asset.GetAsset(); + check(Object != nullptr); + + FLintRunner* Runner = new FLintRunner(Object, this, &Results->Violations, ParentScopedSlowTask); + check(Runner != nullptr); + + LintRunners.Add(Runner); + + if (Runner->RequiresGamethread()) { + Runner->Run(); + // If we're given a scoped slow task, update its progress now... + if (ParentScopedSlowTask != nullptr) { + ParentScopedSlowTask->EnterProgressFrame(1.0f); + } + } else { +#if UE_VERSION_NEWER_THAN(5, 1, 0) + Threads.Push(FRunnableThread::Create(Runner, *FString::Printf(TEXT("FLintRunner - %s"), *Asset.GetObjectPathString()), 0, TPri_Normal)); +#else + Threads.Push(FRunnableThread::Create(Runner, *FString::Printf(TEXT("FLintRunner - %s"), *Asset.ObjectPath.ToString()), 0, TPri_Normal)); +#endif + if (ParentScopedSlowTask != nullptr) { + ParentScopedSlowTask->EnterProgressFrame(1.0f); + } + } + } + + for (FRunnableThread* Thread : Threads) { + Thread->WaitForCompletion(); + } + + if (ParentScopedSlowTask != nullptr) { + ParentScopedSlowTask->EnterProgressFrame(1.0f, NSLOCTEXT("Linter", "ScanTaskFinished", "Tabulating Data...")); + } + + // Count Errors and Warnings + for (const FLintRuleViolation& Violation : Results->Violations) { + if (Violation.ViolatedRule->GetDefaultObject()->RuleSeverity <= ELintRuleSeverity::Error) { + Results->Errors++; + } else { + Results->Warnings++; + } + } + + // Generate Result String + Results->Result = FText::FormatNamed( + FText::FromString("Linted {NumAssets} Assets: {NumWarnings} {NumWarnings}|plural(one=warning,other=warnings), {NumErrors} {NumErrors}|plural(one=error,other=errors)."), + TEXT("NumAssets"), FText::FromString(FString::FromInt(Results->CheckedAssets.Num())), + TEXT("NumWarnings"), FText::FromString(FString::FromInt(Results->Warnings)), + TEXT("NumErrors"), FText::FromString(FString::FromInt(Results->Errors)) + ); + + return Results; +} + +const FLintRuleList* ULintRuleSet::GetLintRuleListForClass(const TSoftClassPtr Class) const { + UClass* SearchClass = Class.LoadSynchronous(); + while (SearchClass != nullptr) { + const FLintRuleList* RuleList = ClassLintRulesMap.Find(SearchClass); + if (RuleList != nullptr) { + return RuleList; + } + + // @HACK: If we reach UObject, find our hack rule for fallback + if (SearchClass == UObject::StaticClass()) { + const FLintRuleList* AnyObjectRuleList = ClassLintRulesMap.Find(UAnyObject_LinterDummyClass::StaticClass()); + return AnyObjectRuleList; + } + + // Load our parent class in case we failed to get naming conventions + SearchClass = SearchClass->GetSuperClass(); + } + + return nullptr; +} + +bool FLintRuleList::RequiresGameThread() const { + for (TSubclassOf LintRuleSubClass : LintRules) { + UClass* LintClass = LintRuleSubClass.Get(); + if (LintClass != nullptr) { + const ULintRule* LintRule = GetDefault(LintClass); + if (LintRule != nullptr && LintRule->bRequiresGameThread) { + return true; + } + } + } + + return false; +} + +bool FLintRuleList::PassesRules(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + OutRuleViolations.Empty(); + + bool bFailedAnyRule = false; + for (TSubclassOf LintRuleSubClass : LintRules) { + UClass* LintClass = LintRuleSubClass.Get(); + if (LintClass != nullptr) { + const ULintRule* LintRule = GetDefault(LintClass); + if (LintRule != nullptr) { + TArray ViolatedRules; + bFailedAnyRule = !LintRule->PassesRule(ObjectToLint, ParentRuleSet, ViolatedRules) || bFailedAnyRule; + OutRuleViolations.Append(ViolatedRules); + } + } + } + + return !bFailedAnyRule; +} diff --git a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp similarity index 50% rename from Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp rename to Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp index f92a617..b1a1090 100644 --- a/Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp @@ -3,19 +3,15 @@ #include "LintRuleSet.h" #include "Engine/Blueprint.h" -ULintRule_Blueprint_Base::ULintRule_Blueprint_Base(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} +ULintRule_Blueprint_Base::ULintRule_Blueprint_Base(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} -bool ULintRule_Blueprint_Base::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const -{ - // If we aren't a blueprint, abort - if (Cast(ObjectToLint) == nullptr) - { - // @TODO: Bubble up some sort of configuration error? - return true; - } +bool ULintRule_Blueprint_Base::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // If we aren't a blueprint, abort + if (Cast(ObjectToLint) == nullptr) { + // @TODO: Bubble up some sort of configuration error? + return true; + } - return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); -} \ No newline at end of file + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp new file mode 100644 index 0000000..fd0cc45 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp @@ -0,0 +1,23 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Compiles.h" +#include "LintRuleSet.h" +#include "Sound/SoundWave.h" +#include "Engine/Blueprint.h" + +ULintRule_Blueprint_Compiles::ULintRule_Blueprint_Compiles(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Compiles::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const UBlueprint* Blueprint = CastChecked(ObjectToLint); + + switch (Blueprint->Status) { + case BS_BeingCreated: + case BS_Dirty: + case BS_Unknown: + case BS_UpToDate: return true; + case BS_Error: + case BS_UpToDateWithWarnings: OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); + return false; + default: return true; + } +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp new file mode 100644 index 0000000..f5098db --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp @@ -0,0 +1,88 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Funcs_MaxNodes.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "K2Node_FunctionResult.h" +#include "K2Node_Knot.h" +#include "K2Node_FunctionEntry.h" +#include "K2Node_Self.h" +#include "K2Node_DynamicCast.h" +#include "K2Node_BreakStruct.h" +#include "EdGraphNode_Comment.h" +#include "K2Node_VariableGet.h" +#include "K2Node_StructMemberGet.h" +#include "K2Node_Tunnel.h" +#include "K2Node_TemporaryVariable.h" +#include "K2Node_FunctionTerminator.h" +#include "K2Node_CastByteToEnum.h" +#include "K2Node_CallFunction.h" + +ULintRule_Blueprint_Funcs_MaxNodes::ULintRule_Blueprint_Funcs_MaxNodes(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Funcs_MaxNodes::IsNodeTrivial(const UEdGraphNode* Node) { + if (Node->IsA(UK2Node_Knot::StaticClass()) + || Node->IsA(UK2Node_FunctionEntry::StaticClass()) + || Node->IsA(UK2Node_Self::StaticClass()) + || Node->IsA(UK2Node_DynamicCast::StaticClass()) + || Node->IsA(UK2Node_BreakStruct::StaticClass()) + || Node->IsA(UEdGraphNode_Comment::StaticClass()) + || Node->IsA(UK2Node_VariableGet::StaticClass()) + || Node->IsA(UK2Node_StructMemberGet::StaticClass()) + || Node->IsA(UK2Node_Tunnel::StaticClass()) + || Node->IsA(UK2Node_TemporaryVariable::StaticClass()) + || Node->IsA(UK2Node_FunctionResult::StaticClass()) + || Node->IsA(UK2Node_FunctionTerminator::StaticClass()) + || Node->IsA(UK2Node_CastByteToEnum::StaticClass()) + ) { + return true; + } + + if (const UK2Node_CallFunction* CallFuncNode = Cast(Node)) { + const FName FuncName = CallFuncNode->FunctionReference.GetMemberName(); + if (FuncName == TEXT("Conv_InterfaceToObject") + || FuncName.ToString().Contains(TEXT("MakeLiteral")) + || FuncName.ToString().Contains(TEXT("ToString")) + || FuncName.ToString().StartsWith(TEXT("Make")) + || FuncName.ToString().StartsWith(TEXT("Break")) + ) { + return true; + } + } + + return false; +} + +bool ULintRule_Blueprint_Funcs_MaxNodes::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsMaxNodes", "{Previous}{WhiteSpace}Please simply function {FuncName} as it has {Nodes} nodes when we want a max of {MaxNodes}."); + FText AllFixes; + + for (auto&& FunctionGraph : Blueprint->FunctionGraphs) { + if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) { + // If initial graph check exceeds node limit, filter out nodes that do not contribute to complexity + if (FunctionGraph->Nodes.Num() > MaxExpectedNonTrivialNodes) { + auto NodesCopy = FunctionGraph->Nodes; + NodesCopy.RemoveAll([this](UEdGraphNode* Val) { + return IsNodeTrivial(Val); + }); + + // If removing knots and comments still exceeds node limit, report error + if (NodesCopy.Num() > MaxExpectedNonTrivialNodes) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("Nodes"), FText::FromString(FString::FromInt(NodesCopy.Num())), TEXT("MaxNodes"), FText::FromString(FString::FromInt(MaxExpectedNonTrivialNodes)), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp new file mode 100644 index 0000000..89595d0 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp @@ -0,0 +1,49 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "K2Node_FunctionResult.h" + +ULintRule_Blueprint_Funcs_MustHaveReturn::ULintRule_Blueprint_Funcs_MustHaveReturn(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Funcs_MustHaveReturn::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + // Early return out if blueprint type shouldn't be checked for return nodes + switch (Blueprint->BlueprintType) { + case BPTYPE_Normal: + case BPTYPE_Const: + case BPTYPE_LevelScript: + case BPTYPE_FunctionLibrary: break; + case BPTYPE_MacroLibrary: + case BPTYPE_Interface: + default: return true; + } + + bool bRuleViolated = false; + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsMustHaveReturn", "{Previous}{WhiteSpace}Please give function {FuncName} a return node."); + FText AllFixes; + + static const FName DefaultAnimGraphName("AnimGraph"); + + for (auto&& FunctionGraph : Blueprint->FunctionGraphs) { + if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript + && FunctionGraph->GetFName() != DefaultAnimGraphName) { + TArray AllResultNodes; + FunctionGraph->GetNodesOfClass(AllResultNodes); + if (AllResultNodes.Num() <= 0) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp new file mode 100644 index 0000000..df2df95 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp @@ -0,0 +1,58 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "K2Node_FunctionEntry.h" + + +ULintRule_Blueprint_Funcs_PublicDescriptions::ULintRule_Blueprint_Funcs_PublicDescriptions(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Funcs_PublicDescriptions::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + // Early return out if blueprint type shouldn't be checked for function descriptions + switch (Blueprint->BlueprintType) { + case BPTYPE_Normal: + case BPTYPE_Const: + case BPTYPE_LevelScript: + case BPTYPE_FunctionLibrary: break; + case BPTYPE_MacroLibrary: + case BPTYPE_Interface: + default: return true; + } + + bool bRuleViolated = false; + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsPublicDescriptions", "{Previous}{WhiteSpace}Please give public function {FuncName} a description."); + FText AllFixes; + + for (const UEdGraph* FunctionGraph : Blueprint->FunctionGraphs) { + if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) { + const UK2Node_FunctionEntry* FunctionEntryNode = nullptr; + TArray EntryNodes; + + FunctionGraph->GetNodesOfClass(EntryNodes); + + if ((EntryNodes.Num() > 0) && EntryNodes[0]->IsEditable()) { + FunctionEntryNode = Cast(EntryNodes[0]); + } + + if (FunctionEntryNode != nullptr) { + if (FUNC_AccessSpecifiers & FunctionEntryNode->GetFunctionFlags() & FUNC_Public) { + if (FunctionEntryNode->MetaData.ToolTip.IsEmpty()) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + } + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp new file mode 100644 index 0000000..fd1ba76 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp @@ -0,0 +1,54 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_LooseNodes.h" +#include "LintRuleSet.h" +#include "Sound/SoundWave.h" +#include "Engine/Blueprint.h" +#include "EdGraph/EdGraphPin.h" +#include "EdGraph/EdGraphNode.h" +#include "EdGraph/EdGraph.h" +#include "EdGraphNode_Comment.h" +#include "K2Node_Event.h" +#include "K2Node_FunctionEntry.h" +#include "K2Node_Knot.h" +#include "K2Node_Tunnel.h" + +ULintRule_Blueprint_LooseNodes::ULintRule_Blueprint_LooseNodes(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_LooseNodes::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const UBlueprint* Blueprint = CastChecked(ObjectToLint); + + // Check for loose nodes + TArray Graphs; + Blueprint->GetAllGraphs(Graphs); + + for (UEdGraph* Graph : Graphs) { + for (const UEdGraphNode* Node : Graph->Nodes) { + if (Node->IsAutomaticallyPlacedGhostNode() || + Node->IsA(UK2Node_Event::StaticClass()) || + Node->IsA(UK2Node_FunctionEntry::StaticClass()) || + Node->IsA(UK2Node_Knot::StaticClass()) || + Node->IsA(UEdGraphNode_Comment::StaticClass()) || + Node->IsA(UK2Node_Tunnel::StaticClass())) { + continue; + } + + bool bNodeIsolated = true; + + TArray Pins = Node->GetAllPins(); + for (const UEdGraphPin* Pin : Pins) { + if (Pin->LinkedTo.Num() != 0) { + bNodeIsolated = false; + break; + } + } + + if (bNodeIsolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); + return false; + } + } + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp new file mode 100644 index 0000000..fe88abc --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp @@ -0,0 +1,56 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "LintRules/LintRule_Blueprint_Vars_ConfigCategories.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "Kismet2/BlueprintEditorUtils.h" + +ULintRule_Blueprint_Vars_ConfigCategories::ULintRule_Blueprint_Vars_ConfigCategories(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Vars_ConfigCategories::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsConfigCategories", "{Previous}{WhiteSpace}Please give variable {VarName} a category."); + const FText FixTextTemplateEditable = NSLOCTEXT("Linter", "BlueprintVarsConfigCategoriesEditable", "{Previous}{WhiteSpace}Please give editable variable {VarName} a category starting with 'Config'."); + FText AllFixes; + + int32 VariableCount = Blueprint->NewVariables.Num(); + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + if (FBlueprintEditorUtils::IsVariableComponent(Desc)) { + VariableCount--; + } + } + + if (VariableCount < NumVariablesToRequireCategorization) { + return true; + } + + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + FString PropName = Desc.VarName.ToString(); + FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); + + // Is Editable variable? + if ((Desc.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) { + if (!Desc.Category.ToString().StartsWith(TEXT("Config"))) { + AllFixes = FText::FormatNamed(FixTextTemplateEditable, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } else { + if (Desc.Category.IsEmptyOrWhitespace()) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp new file mode 100644 index 0000000..77be1d3 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp @@ -0,0 +1,36 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" + +ULintRule_Blueprint_Vars_EditableMustHaveTooltip::ULintRule_Blueprint_Vars_EditableMustHaveTooltip(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Vars_EditableMustHaveTooltip::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsEditableMustHaveTooltip", "{Previous}{WhiteSpace}Please give variable {VarName} a tooltip as it is marked editable."); + FText AllFixes; + + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + FString PropName = Desc.VarName.ToString(); + FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); + + if ((Desc.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) { + if (!Desc.HasMetaData(FBlueprintMetadata::MD_Tooltip) || Desc.GetMetaData(FBlueprintMetadata::MD_Tooltip).Len() <= 0) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp new file mode 100644 index 0000000..48107ba --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp @@ -0,0 +1,34 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" + +ULintRule_Blueprint_Vars_NoConfigFlag::ULintRule_Blueprint_Vars_NoConfigFlag(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Vars_NoConfigFlag::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsNoConfigFlag", "{Previous}{WhiteSpace}Please disable the config flag on variable {VarName}."); + FText AllFixes; + + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + FString PropName = Desc.VarName.ToString(); + FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); + + if ((Desc.PropertyFlags & CPF_Config) == CPF_Config) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp new file mode 100644 index 0000000..bcf52ac --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp @@ -0,0 +1,46 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Vars_NonAtomic.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "Internationalization/Regex.h" + +ULintRule_Blueprint_Vars_NonAtomic::ULintRule_Blueprint_Vars_NonAtomic(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + + +bool ULintRule_Blueprint_Vars_NonAtomic::IsVariableAtomic(FBPVariableDescription& VarDesc) { + return (VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean + || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Byte + || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Int + || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Float + || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_String + || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Enum + ); +} + +bool ULintRule_Blueprint_Vars_NonAtomic::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsNonAtomic", "{Previous}{WhiteSpace}Please fix variable named {VarName}."); + FText AllFixes; + + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + FString PropName = Desc.VarName.ToString(); + FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); + + if (IsVariableAtomic(Desc) && PropName.Contains(TypeName.ToString())) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp new file mode 100644 index 0000000..f852dcb --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp @@ -0,0 +1,34 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Vars_PluralArrays.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" + +ULintRule_Blueprint_Vars_PluralArrays::ULintRule_Blueprint_Vars_PluralArrays(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Blueprint_Vars_PluralArrays::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "PluralArrayHasArray", "{Previous}{WhiteSpace}Please remove the word 'Array' from your variable {VarName}."); + FText AllFixes; + + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + FString PropName = Desc.VarName.ToString(); + FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); + + if (PropName.Contains(TEXT("Array"), ESearchCase::CaseSensitive)) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp new file mode 100644 index 0000000..92536e0 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp @@ -0,0 +1,46 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Blueprint_Vars_Regex.h" +#include "LintRuleSet.h" +#include "Engine/Blueprint.h" +#include "EdGraphSchema_K2.h" +#include "Internationalization/Regex.h" + +ULintRule_Blueprint_Vars_Regex::ULintRule_Blueprint_Vars_Regex(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + + +bool ULintRule_Blueprint_Vars_Regex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UBlueprint* Blueprint = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsRegex", "{Previous}{WhiteSpace}Please fix variable named {VarName}."); + FText AllFixes; + + const FString TestRegexPatternString = RegexPatternString; + const FString BoolTestRegexPatternString = TEXT("b") + RegexPatternString; + + const FRegexPattern TestRegexPattern = FRegexPattern(TestRegexPatternString); + const FRegexPattern BoolTestRegexPattern = FRegexPattern(BoolTestRegexPatternString); + + for (FBPVariableDescription Desc : Blueprint->NewVariables) { + FString PropName = Desc.VarName.ToString(); + FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); + const bool bIsBool = Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean; + + FRegexMatcher Matcher(bIsBool ? BoolTestRegexPattern : TestRegexPattern, PropName); + const bool bFoundMatch = Matcher.FindNext(); + + if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Collection.cpp b/Source/Linter/Private/LintRules/LintRule_Collection.cpp new file mode 100644 index 0000000..fe2ae97 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Collection.cpp @@ -0,0 +1,25 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Collection.h" +#include "LintRuleSet.h" + +ULintRule_Collection::ULintRule_Collection(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Collection::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + bool bRuleViolated = false; + + for (TSubclassOf LintRuleClass : SubRules) { + if (LintRuleClass.Get() != nullptr) { + const ULintRule* LintRule = GetDefault(LintRuleClass); + if (LintRule != nullptr) { + TArray SubViolations; + if (!LintRule->PassesRule(ObjectToLint, ParentRuleSet, SubViolations)) { + OutRuleViolations.Append(SubViolations); + bRuleViolated = true; + } + } + } + } + + return !bRuleViolated; +} diff --git a/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp b/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp new file mode 100644 index 0000000..274b9e6 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp @@ -0,0 +1,89 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_IsNamedCorrectly_Base.h" +#include "LintRuleSet.h" +#include "LinterNamingConvention.h" + +ULintRule_IsNamedCorrectly_Base::ULintRule_IsNamedCorrectly_Base(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_IsNamedCorrectly_Base::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // NameSettingList Contributed by RocknRolla#3102 on http://discord.gamemak.in + TArray NameSettingList; + // If ObjectToLint is a Blueprint, it's class hierarchy does not contain actual classes that Blueprints extend (like Actor, Character, PlayerController, etc.). + // So we try to use Blueprint's ParentClass to access it's class hierarchy. + if (ObjectToLint->IsA()) { + const TSubclassOf BlueprintClass = Cast(ObjectToLint)->ParentClass; + if (BlueprintClass != nullptr) { + NameSettingList = ParentRuleSet->GetNamingConvention()->GetNamingConventionsForClassVariant(TSoftClassPtr(BlueprintClass), GetRuleBasedObjectVariantName(ObjectToLint)); + } + } + // If ObjectToLint is not a Blueprint or we failed to find any conventions, just fall back to our default algorithm. + if (NameSettingList.Num() == 0) { + NameSettingList = ParentRuleSet->GetNamingConvention()->GetNamingConventionsForClassVariant(ObjectToLint->GetClass(), GetRuleBasedObjectVariantName(ObjectToLint)); + } + + // If we don't have a name rule for this type of asset, simply return true + if (NameSettingList.Num() == 0) { + return true; + } + + bool bFoundMatchingNameRule = false; + for (FLinterNamingConventionInfo Info : NameSettingList) { + const bool bPassesPrefixCheck = Info.Prefix.IsEmpty() ? true : ObjectToLint->GetName().StartsWith(Info.Prefix, ESearchCase::CaseSensitive); + const bool bPassesSuffixCheck = Info.Suffix.IsEmpty() ? true : ObjectToLint->GetName().EndsWith(Info.Suffix, ESearchCase::CaseSensitive); + // Run prefix and suffix checks using found name settings if they are non-null + + if (bPassesPrefixCheck && bPassesSuffixCheck) { + bFoundMatchingNameRule = true; + break; + } + } + + if (!bFoundMatchingNameRule) { + const FString SuggestedName = BuildSuggestedName(ObjectToLint->GetName(), NameSettingList[0].Prefix, NameSettingList[0].Suffix); + const FText RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "IsNamedCorrectly_RecommendedAction", "Recommended name: [{0}]."), FText::FromString(SuggestedName)); + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); + return false; + } + + // If we don't have name settings or passed all name checks, simply return true + return true; +} + +FString ULintRule_IsNamedCorrectly_Base::BuildSuggestedName(FString CurrentName, FString DesiredPrefix, FString DesiredSuffix /*= TEXT("")*/) { + FString SuggestedName; + + const int32 FirstUnderscore = CurrentName.Find(TEXT("_")); + const int32 LastUnderscore = CurrentName.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd); + + bool bAddPrefix = false; + bool bAddSuffix = false; + + // Attempt to remove a bad prefix + if (!DesiredPrefix.IsEmpty() && !CurrentName.StartsWith(DesiredPrefix, ESearchCase::CaseSensitive)) { + bAddPrefix = true; + if (FirstUnderscore <= 3) { + CurrentName = CurrentName.RightChop(FirstUnderscore + 1); + } + } + + // Attempt to remove a bad suffix + if (!DesiredSuffix.IsEmpty() && !CurrentName.EndsWith(DesiredSuffix, ESearchCase::CaseSensitive)) { + bAddSuffix = true; + if (CurrentName.Len() - LastUnderscore <= 3) { + CurrentName = CurrentName.LeftChop(CurrentName.Len() - LastUnderscore); + } + } + + SuggestedName = CurrentName; + + if (bAddPrefix) { + SuggestedName = DesiredPrefix + SuggestedName; + } + + if (bAddSuffix) { + SuggestedName = SuggestedName + DesiredSuffix; + } + + return SuggestedName; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Level_LightingNeedsToRebuilt.cpp b/Source/Linter/Private/LintRules/LintRule_Level_LightingNeedsToRebuilt.cpp new file mode 100644 index 0000000..9d2e0eb --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Level_LightingNeedsToRebuilt.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2021-2025, Forschungszentrum Jülich GmbH. All rights reserved. + + +#include "LintRules/LintRule_Level_LightingNeedsToRebuilt.h" +#include "EngineModule.h" + +ULintRule_Level_LightingNeedsToRebuilt::ULintRule_Level_LightingNeedsToRebuilt(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { + bRequiresGameThread = true; +} + +bool ULintRule_Level_LightingNeedsToRebuilt::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + if (!Cast(ObjectToLint)) { + return true; + } + + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule_Level_LightingNeedsToRebuilt::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + UWorld* World = CastChecked(ObjectToLint); + + GetRendererModule().UpdateMapNeedsLightingFullyRebuiltState(World); + if (World->NumLightingUnbuiltObjects > 0 || World->NumUnbuiltReflectionCaptures > 0) { + const FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_Level_LightingNeedsToRebuilt", "Rebuild the Lighting of {0}"); + OutRuleViolations.Push( + FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, FText::FromString(World->GetMapName()))) + ); + return false; + } + + return true; +} \ No newline at end of file diff --git a/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp b/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp new file mode 100644 index 0000000..4aecd5c --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp @@ -0,0 +1,53 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_ParticleSystem_EmitterNameRegex.h" +#include "LintRuleSet.h" +#include "Particles/ParticleEmitter.h" +#include "Particles/ParticleSystem.h" +#include "Internationalization/Regex.h" + +ULintRule_ParticleSystem_EmitterNameRegex::ULintRule_ParticleSystem_EmitterNameRegex(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer), + RegexPatternString(TEXT("Particle Emitter")) { + DisallowedRecommendedAction = NSLOCTEXT("Linter", "ULintRule_ParticleSystem_EmitterRegexName_Disallowed", "Please rename the emitter \"{0}\" as you have multiple emitters."); + NonConformingRecommendedAction = NSLOCTEXT("Linter", "ULintRule_ParticleSystem_EmitterRegexName_NonConforming", "Please rename \"{0}\" as this emitter has invalid characters."); +} + +bool ULintRule_ParticleSystem_EmitterNameRegex::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // If we aren't a particle system, abort + if (Cast(ObjectToLint) == nullptr) { + // @TODO: Bubble up some sort of configuration error? + return true; + } + + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule_ParticleSystem_EmitterNameRegex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const UParticleSystem* ParticleSystem = CastChecked(ObjectToLint); + + bool bRuleViolated = false; + + const FText FixTextTemplate = NSLOCTEXT("Linter", "ParticleHasBadEmitterNames", "{Previous}{WhiteSpace}Please rename emitter {EmitterName}."); + FText AllFixes; + + if (ParticleSystem->Emitters.Num() >= MinEmittersNeededToEnforce) { + const FRegexPattern RegexPattern = FRegexPattern(RegexPatternString); + + for (const UParticleEmitter* Emitter : ParticleSystem->Emitters) { + FRegexMatcher RegexMatcher(RegexPattern, Emitter->EmitterName.ToString()); + const bool bFoundMatch = RegexMatcher.FindNext(); + + if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) { + AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("EmitterName"), FText::FromString(Emitter->EmitterName.ToString()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); + bRuleViolated = true; + } + } + } + + if (bRuleViolated) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp b/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp new file mode 100644 index 0000000..32dfabc --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp @@ -0,0 +1,25 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Path_DisallowNames.h" +#include "LintRuleSet.h" + +ULintRule_Path_DisallowNames::ULintRule_Path_DisallowNames(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + RecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_DisallowNames_ChangeName", "Please rename \"{0}\" to an allowed name."); +} + +bool ULintRule_Path_DisallowNames::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const FString PathName = ObjectToLint->GetPathName(); + TArray PathElements; + PathName.ParseIntoArray(PathElements, TEXT("/"), true); + + bool bRuleViolated = false; + + for (int32 i = 0; i < PathElements.Num() - 1; ++i) { + if (DisallowedFolderNames.Contains(PathElements[i])) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, FText::FromString(PathElements[i])))); + bRuleViolated = true; + } + } + + return !bRuleViolated; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp b/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp new file mode 100644 index 0000000..668066f --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp @@ -0,0 +1,22 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Path_IsNotTooLong.h" +#include "LintRuleSet.h" + +ULintRule_Path_IsNotTooLong::ULintRule_Path_IsNotTooLong(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Path_IsNotTooLong::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const FString PathName = ObjectToLint->GetPathName(); + + // See if file path is longer than 140 characters + // 145 = 140 + /Game (5) + int32 DotIndex = -1; + PathName.FindLastChar('.', DotIndex); + const FString FilePath = PathName.LeftChop(PathName.Len() - DotIndex); + if (FilePath.Len() >= MaxPathLimit + 5) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp b/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp new file mode 100644 index 0000000..0caef7d --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp @@ -0,0 +1,53 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Path_NoTopLevel.h" +#include "LintRuleSet.h" +#include "HAL/FileManager.h" + +ULintRule_Path_NoTopLevel::ULintRule_Path_NoTopLevel(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + ZeroTopLevelFoldersRecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_NoTopLevel_ZeroTopLevelFolders", "There appears to be no top level folders. Please put your assets in a top level folder."); + PleaseUseThisFolderRecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_NoTopLevel_PleaseUseThisFolder", "Please move this asset into a top level folder. Maybe \"{0}\"?"); +} + +bool ULintRule_Path_NoTopLevel::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const FString PathName = ObjectToLint->GetPathName(); + TArray PathElements; + PathName.ParseIntoArray(PathElements, TEXT("/"), true); + + // Report issue with top assets not in a top level folder + if (PathElements.Num() == 1) { + FText RecommendedAction; + + // This is really slow to do for every single asset that fails to be in a top level folder + // But doing it here makes the code base a lot cleaner and easier to follow for now. + { + // Determine content sub directory structure for project organization based rules + TArray Subdirectories; + IFileManager::Get().FindFiles(Subdirectories, *(FPaths::ProjectContentDir() / TEXT("*")), false, true); + Subdirectories.Remove(TEXT("Collections")); + Subdirectories.Remove(TEXT("Developers")); + + if (Subdirectories.Num() == 0) { + RecommendedAction = ZeroTopLevelFoldersRecommendedAction; + } else { + FString MostPopulatedContentDir; + int32 FileCount = 0; + for (FString Subdirectory : Subdirectories) { + TArray FileNames; + IFileManager::Get().FindFilesRecursive(FileNames, *(FPaths::ProjectContentDir() / Subdirectory), TEXT("*"), true, false, false); + if (FileNames.Num() > FileCount) { + FileCount = FileNames.Num(); + MostPopulatedContentDir = Subdirectory; + } + } + + RecommendedAction = FText::FormatOrdered(PleaseUseThisFolderRecommendedAction, FText::FromString(MostPopulatedContentDir)); + } + } + + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp b/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp new file mode 100644 index 0000000..e111c98 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp @@ -0,0 +1,46 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Path_Regex.h" +#include "LintRuleSet.h" +#include "Internationalization/Regex.h" + +ULintRule_Path_Regex::ULintRule_Path_Regex(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer), + RegexPatternString(TEXT("[^a-zA-Z0-9_]")) { + DisallowedPathElementRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_DisallowedPathElement", "Please rename \"{0}\" and remove disallowed characters."); + NonConformingPathElementRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_NonConformingPathElement", "Please rename \"{0}\" and to conform to allowed characters."); + + DisallowedWholePathRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_DisallowedWholePath", "Please rename and remove disallowed characters."); + NonConformingWholePathRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_NonConformingWholePath", "Please rename and conform to allowed characters."); +} + +bool ULintRule_Path_Regex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const FString PathName = ObjectToLint->GetPathName(); + + const FRegexPattern RegexPattern = FRegexPattern(RegexPatternString); + bool bRuleViolated = false; + + if (bCheckPerPathElement) { + TArray PathElements; + PathName.ParseIntoArray(PathElements, TEXT("/"), true); + + for (int32 i = 0; i < PathElements.Num() - 1; ++i) { + FRegexMatcher RegexMatcher(RegexPattern, PathElements[i]); + const bool bFoundMatch = RegexMatcher.FindNext(); + + if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(bMustNotContainRegexPattern ? DisallowedPathElementRecommendedAction : NonConformingPathElementRecommendedAction, FText::FromString(PathElements[i])))); + bRuleViolated = true; + } + } + } else { + FRegexMatcher RegexMatcher(RegexPattern, PathName); + const bool bFoundMatch = RegexMatcher.FindNext(); + + if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) { + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(bMustNotContainRegexPattern ? DisallowedWholePathRecommendedAction : NonConformingWholePathRecommendedAction, FText::FromString(PathName)))); + bRuleViolated = true; + } + } + + return !bRuleViolated; +} diff --git a/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp b/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp new file mode 100644 index 0000000..9bf3060 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp @@ -0,0 +1,35 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_SoundWave_SampleRate.h" +#include "LintRuleSet.h" +#include "Sound/SoundWave.h" + +ULintRule_SoundWave_SampleRate::ULintRule_SoundWave_SampleRate(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + ValidSampleRates.Push(22050.0f); + ValidSampleRates.Push(44100.0f); +} + +bool ULintRule_SoundWave_SampleRate::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // If we aren't a sound wave, abort + if (Cast(ObjectToLint) == nullptr) { + // @TODO: Bubble up some sort of configuration error? + return true; + } + + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule_SoundWave_SampleRate::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + USoundWave* SoundWave = CastChecked(ObjectToLint); + + if (ValidSampleRates.Contains(SoundWave->GetSampleRateForCurrentPlatform())) { + return true; + } + + FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_SoundWave_SampleRate_Fix", "Please fix your sample rate of {0}."); + RecommendedAction = FText::FormatOrdered(RecommendedAction, SoundWave->GetSampleRateForCurrentPlatform()); + + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp b/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp new file mode 100644 index 0000000..95a2d79 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp @@ -0,0 +1,44 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_StaticMesh_ValidUVs.h" +#include "LintRuleSet.h" + +ULintRule_StaticMesh_ValidUVs::ULintRule_StaticMesh_ValidUVs(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + //UStaticMesh::CheckLightMapUVs requires being ran on the game thread + bRequiresGameThread = true; +} + +bool ULintRule_StaticMesh_ValidUVs::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // If we aren't a static mesh, abort + if (Cast(ObjectToLint) == nullptr) { + // @TODO: Bubble up some sort of configuration error? + return true; + } + + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule_StaticMesh_ValidUVs::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const UStaticMesh* StaticMesh = CastChecked(ObjectToLint); + + TArray MissingUVs; + TArray BadUVs; + TArray ValidUVs; + + UStaticMesh::CheckLightMapUVs(const_cast(StaticMesh), MissingUVs, BadUVs, ValidUVs, true); + + if ((!bIgnoreMissingUVs && MissingUVs.Num() > 0)) { + const FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_StaticMesh_ValidUVs_Missing", "Static mesh has missing UVs. Please add at least one valid UV channel."); + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); + return false; + } + + if (BadUVs.Num() > 0) { + const FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_StaticMesh_ValidUVs_Bad", "Static mesh has invalid UVs. [{0}]"); + FText::FormatOrdered(RecommendedAction, FText::FromString(FString::Join(BadUVs, TEXT(", ")))); + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp b/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp new file mode 100644 index 0000000..4e4db94 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp @@ -0,0 +1,41 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Texture_Size_NotTooBig.h" + +#include "Linter.h" +#include "LintRuleSet.h" + +ULintRule_Texture_Size_NotTooBig::ULintRule_Texture_Size_NotTooBig(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) {} + +bool ULintRule_Texture_Size_NotTooBig::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // If we aren't a texture, abort + if (Cast(ObjectToLint) == nullptr) { + // @TODO: Bubble up some sort of configuration error? + return true; + } + + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule_Texture_Size_NotTooBig::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const UTexture2D* Texture = CastChecked(ObjectToLint); + + // ToDo: Make Texture->GetSizeX() work again (possibly Bug in 5.3?) + const FTexturePlatformData* PlatformData = Texture->GetPlatformData(); + if (!PlatformData) { + UE_LOG(LogLinter, Warning, TEXT("Could not get Platform Data for Texture!")) + return true; + } + + int32 TexSizeX = PlatformData->SizeX; + int32 TexSizeY = PlatformData->SizeY; + + // Check to see if textures are too big + if (TexSizeX > MaxTextureSizeX || TexSizeY > MaxTextureSizeY) { + const FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_Texture_Size_NotTooBig_TooBig", "Please shrink your textures dimensions so that they fit within {0}x{1} pixels."); + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, MaxTextureSizeX, MaxTextureSizeY))); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp b/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp new file mode 100644 index 0000000..50259a1 --- /dev/null +++ b/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp @@ -0,0 +1,68 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "LintRules/LintRule_Texture_Size_PowerOfTwo.h" + +#include "Linter.h" +#include "LintRuleSet.h" + +ULintRule_Texture_Size_PowerOfTwo::ULintRule_Texture_Size_PowerOfTwo(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + IgnoreTexturesInTheseGroups.Add(TEXTUREGROUP_UI); +} + +bool ULintRule_Texture_Size_PowerOfTwo::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + // If we aren't a texture, abort + if (Cast(ObjectToLint) == nullptr) { + // @TODO: Bubble up some sort of configuration error? + return true; + } + + // If we're to ignore this texture LOD group, abort + if (IgnoreTexturesInTheseGroups.Contains(Cast(ObjectToLint)->LODGroup)) { + return true; + } + + return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); +} + +bool ULintRule_Texture_Size_PowerOfTwo::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const { + const UTexture2D* Texture = CastChecked(ObjectToLint); + + // ToDo: Make Texture->GetSizeX() work again (possibly Bug in 5.3?) + const FTexturePlatformData* PlatformData = Texture->GetPlatformData(); + if (!PlatformData) { + UE_LOG(LogLinter, Warning, TEXT("Could not get Platform Data for Texture!")) + return true; + } + + int32 TexSizeX = PlatformData->SizeX; + int32 TexSizeY = PlatformData->SizeY; + + const bool bXFail = ((TexSizeX & (TexSizeX - 1)) != 0); + const bool bYFail = ((TexSizeY & (TexSizeY - 1)) != 0); + + const UEnum* TextureGroupEnum = StaticEnum(); + FString IgnoredLODGroupNames; + + for (TEnumAsByte LODGroup : IgnoreTexturesInTheseGroups) { + IgnoredLODGroupNames += TextureGroupEnum->GetMetaData(TEXT("DisplayName"), LODGroup) + TEXT(", "); + } + IgnoredLODGroupNames.RemoveFromEnd(TEXT(", ")); + + FText IgnoredLODGroupTip = IgnoredLODGroupNames.Len() > 0 ? FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_AllowedLODGroups", ". Alternatively, assign this texture to one of these LOD Groups: [{0}]"), FText::FromString(IgnoredLODGroupNames)) : FText::GetEmpty(); + + if (bXFail || bYFail) { + FText RecommendedAction; + if (bXFail && bYFail) { + RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_XY", "Please fix the width and height of this texture, currently {0} by {1}{2}"), TexSizeX, TexSizeY, IgnoredLODGroupTip); + } else if (bXFail) { + RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_X", "Please fix the width of this texture, currently {0}{1}"), TexSizeX, IgnoredLODGroupTip); + } else if (bYFail) { + RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_Y", "Please fix the height of this texture, currently {0}{1}"), TexSizeY, IgnoredLODGroupTip); + } + + OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); + return false; + } + + return true; +} diff --git a/Source/Linter/Private/LintRunner.cpp b/Source/Linter/Private/LintRunner.cpp new file mode 100644 index 0000000..8182e27 --- /dev/null +++ b/Source/Linter/Private/LintRunner.cpp @@ -0,0 +1,70 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#include "LintRunner.h" +#include "Linter.h" +#include "LintRuleSet.h" + +#define LOCTEXT_NAMESPACE "Linter" + +FCriticalSection FLintRunner::LintDataUpdateLock; + +FLintRunner::FLintRunner(UObject* InLoadedObject, const ULintRuleSet* LintRuleSet, TArray* InpOutRuleViolations, FScopedSlowTask* InParentScopedSlowTask) : + LoadedObject(InLoadedObject), + RuleSet(LintRuleSet), + pOutRuleViolations(InpOutRuleViolations), + pLoadedRuleList(LintRuleSet != nullptr ? LintRuleSet->GetLintRuleListForClass(InLoadedObject->GetClass()) : nullptr), + ParentScopedSlowTask(InParentScopedSlowTask) {} + +bool FLintRunner::RequiresGamethread() { + if (pLoadedRuleList != nullptr) { + return pLoadedRuleList->RequiresGameThread(); + } + + return false; +} + +bool FLintRunner::Init() { + if (LoadedObject == nullptr) { + return false; + } + + if (RuleSet == nullptr) { + return false; + } + + if (pLoadedRuleList == nullptr) { + return false; + } + + if (pOutRuleViolations == nullptr) { + return false; + } + + return true; +} + +uint32 FLintRunner::Run() { + if (LoadedObject == nullptr || pLoadedRuleList == nullptr || RuleSet == nullptr || pOutRuleViolations == nullptr) { + return 2; + } + + const FString AssetPath = LoadedObject->GetPathName(); + UE_LOG(LogLinter, Display, TEXT("Loaded '%s'..."), *AssetPath); + + TArray RuleViolations; + pLoadedRuleList->PassesRules(LoadedObject, RuleSet, RuleViolations); + + if (RuleViolations.Num() > 0) { + FScopeLock Lock(&LintDataUpdateLock); + pOutRuleViolations->Append(RuleViolations); + } + + UE_LOG(LogLinter, Display, TEXT("Finished '%s'..."), *AssetPath); + return 0; +} + +void FLintRunner::Stop() {} + +void FLintRunner::Exit() {} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/Linter.cpp b/Source/Linter/Private/Linter.cpp new file mode 100644 index 0000000..fa3f8c7 --- /dev/null +++ b/Source/Linter/Private/Linter.cpp @@ -0,0 +1,122 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#include "Linter.h" +#include "ISettingsModule.h" +#include "Framework/Docking/TabManager.h" +#include "Styling/SlateStyle.h" +#include "PropertyEditorModule.h" +#include "LinterStyle.h" +#include "LinterContentBrowserExtensions.h" +#include "LinterNamingConvention.h" +#include "LinterSettings.h" +#include "UI/LintWizard.h" +#include "LintRuleSet.h" +#include "AssetRegistry/AssetRegistryModule.h" + +#define LOCTEXT_NAMESPACE "FLinterModule" + + +static const FName LinterTabName = "LinterTab"; + + +void FLinterModule::StartupModule() { + // Load the asset registry module + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + if (AssetRegistry.IsLoadingAssets()) { + AssetRegistry.OnFilesLoaded().AddRaw(this, &FLinterModule::OnInitialAssetRegistrySearchComplete); + } else { + OnInitialAssetRegistrySearchComplete(); + } + + // Integrate Linter actions into existing editor context menus + if (!IsRunningCommandlet()) { + // Register slate style overrides + FLinterStyle::Initialize(); + const TSharedPtr StyleSetPtr = FLinterStyle::StyleSet; + + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) { + SettingsModule->RegisterSettings("Project", "Plugins", "Linter", + LOCTEXT("RuntimeSettingsName", "Linter"), + LOCTEXT("RuntimeSettingsDescription", "Configure the Linter plugin"), + GetMutableDefault()); + } + + // Install UI Hooks + FLinterContentBrowserExtensions::InstallHooks(); + + //Register our UI + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(LinterTabName, FOnSpawnTab::CreateStatic(&FLinterModule::SpawnTab, StyleSetPtr)) + .SetDisplayName(LOCTEXT("LinterTabName", "Linter")) + .SetTooltipText(LOCTEXT("LinterTabToolTip", "Linter")) + .SetMenuType(ETabSpawnerMenuType::Hidden); + + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + PropertyModule.RegisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FLinterNamingConventionDetails::MakeInstance)); + } +} + +void FLinterModule::ShutdownModule() { + if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) { + SettingsModule->UnregisterSettings("Project", "Plugins", "Linter"); + } + + if (UObjectInitialized()) { + FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); + PropertyModule.UnregisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName()); + + // Remove Hooks and Tabs + FLinterContentBrowserExtensions::RemoveHooks(); + FGlobalTabmanager::Get()->UnregisterTabSpawner(LinterTabName); + + // Unregister slate style overrides + FLinterStyle::Shutdown(); + } +} + + +TSharedRef FLinterModule::SpawnTab(const FSpawnTabArgs& TabSpawnArgs, TSharedPtr StyleSet) { + const FSlateBrush* IconBrush = StyleSet->GetBrush("Linter.Toolbar.Icon"); + + // clang-format off + // @formatter:off + const TSharedRef MajorTab = + SNew(SDockTab) + .TabRole(ETabRole::MajorTab); + // clang-format on + // @formatter:on + + MajorTab->SetContent(SNew(SLintWizard)); + MajorTab->SetTabIcon(IconBrush); + + return MajorTab; +} + +void FLinterModule::OnInitialAssetRegistrySearchComplete() { + TryToLoadAllLintRuleSets(); +} + +void FLinterModule::TryToLoadAllLintRuleSets() { + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); + const IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + TArray FoundRuleSets; +#if UE_VERSION_NEWER_THAN(5, 1, 0) + AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetClassPathName(), FoundRuleSets, true); +#else + AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); +#endif + + // Attempt to get all RuleSets in memory so that linting tools are better aware of them + for (const FAssetData& RuleSetData : FoundRuleSets) { + if (!RuleSetData.IsAssetLoaded()) { + (void)RuleSetData.GetAsset(); + } + } +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FLinterModule, Linter) +DEFINE_LOG_CATEGORY(LogLinter); diff --git a/Source/Linter/Private/LinterCommandlet.cpp b/Source/Linter/Private/LinterCommandlet.cpp new file mode 100644 index 0000000..d368a80 --- /dev/null +++ b/Source/Linter/Private/LinterCommandlet.cpp @@ -0,0 +1,157 @@ +// Copyright 2020 Gamemakin LLC. All Rights Reserved. + +#include "LinterCommandlet.h" + +#include "AssetRegistry/AssetRegistryModule.h" +#include "Dom/JsonObject.h" +#include "Dom/JsonValue.h" +#include "Interfaces/IPluginManager.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" +#include "Serialization/JsonWriter.h" +#include "Serialization/JsonSerializer.h" +#include "Linter.h" +#include "LinterSettings.h" +#include "LintRule.h" +#include "LintRuleSet.h" + +DEFINE_LOG_CATEGORY_STATIC(LinterCommandlet, All, All); + +ULinterCommandlet::ULinterCommandlet(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + IsClient = false; + IsServer = false; +} + +static void PrintUsage() { + UE_LOG(LinterCommandlet, Display, TEXT("Linter Usage: {Editor}.exe Project.uproject -run=Linter \"/Game/\"")); + UE_LOG(LinterCommandlet, Display, TEXT("")); + UE_LOG(LinterCommandlet, Display, TEXT("This will run the Linter on the provided project and will scan the supplied directory, example being the project's full Content/Game tree. Can add multiple paths as additional arguments.")); +} + +int32 ULinterCommandlet::Main(const FString& InParams) { + FString Params = InParams; + // Parse command line. + TArray Paths; + TArray Switches; + TMap ParamsMap; + ParseCommandLine(*Params, Paths, Switches, ParamsMap); + + UE_LOG(LinterCommandlet, Display, TEXT("Linter is indeed running!")); + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + + UE_LOG(LinterCommandlet, Display, TEXT("Loading the asset registry...")); + AssetRegistryModule.Get().SearchAllAssets(/*bSynchronousSearch =*/true); + UE_LOG(LinterCommandlet, Display, TEXT("Finished loading the asset registry. Determining Rule Set...")); + + ULintRuleSet* RuleSet = GetDefault()->DefaultLintRuleSet.LoadSynchronous(); + if (ParamsMap.Contains(TEXT("RuleSet"))) { + const FString RuleSetName = *ParamsMap.FindChecked(TEXT("RuleSet")); + UE_LOG(LinterCommandlet, Display, TEXT("Trying to find Rule Set with Commandlet Name: %s"), *RuleSetName); + + FLinterModule::TryToLoadAllLintRuleSets(); + + TArray FoundRuleSets; +#if UE_VERSION_NEWER_THAN(5, 1, 0) + AssetRegistryModule.Get().GetAssetsByClass(ULintRuleSet::StaticClass()->GetClassPathName(), FoundRuleSets, true); +#else + AssetRegistryModule.Get().GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); +#endif + + for (const FAssetData& RuleSetData : FoundRuleSets) { + ULintRuleSet* LoadedRuleSet = Cast(RuleSetData.GetAsset()); + if (LoadedRuleSet != nullptr && LoadedRuleSet->NameForCommandlet == RuleSetName) { + RuleSet = LoadedRuleSet; + UE_LOG(LinterCommandlet, Display, TEXT("Found Rule Set for name %s: %s"), *RuleSetName, *RuleSet->GetFullName()); + } + } + } else { + UE_LOG(LinterCommandlet, Display, TEXT("Using default rule set...")); + } + + if (RuleSet == nullptr) { + UE_LOG(LinterCommandlet, Error, TEXT("Failed to load a rule set. Aborting. Returning error code 1.")); + return 1; + } + UE_LOG(LinterCommandlet, Display, TEXT("Using rule set: %s"), *RuleSet->GetFullName()); + + if (Paths.Num() == 0) { + Paths.Add(TEXT("/Game")); + } + UE_LOG(LinterCommandlet, Display, TEXT("Attempting to Lint paths: %s"), *FString::Join(Paths, TEXT(", "))); + + const ULintResults* LintResults = RuleSet->LintPath(Paths); + UE_LOG(LinterCommandlet, Display, TEXT("%s"), *LintResults->Result.ToString()); + + if (Switches.Contains("json") || ParamsMap.Contains("json") || Switches.Contains("html") || ParamsMap.Contains("html")) { + UE_LOG(LinterCommandlet, Display, TEXT("Generating output report...")); + + // Write JSON file if requested + if (Switches.Contains("json") || ParamsMap.Contains(FString("json"))) { + FDateTime Now = FDateTime::Now(); + FString JsonOutputName = "lint-report-" + Now.ToString() + ".json"; + + const FString LintReportPath = FPaths::ProjectSavedDir() / "LintReports"; + FString FullOutputPath = LintReportPath / JsonOutputName; + + if (ParamsMap.Contains("json")) { + const FString JsonOutputOverride = *ParamsMap.FindChecked(FString("json")); + if (FPaths::IsRelative(JsonOutputOverride)) { + FullOutputPath = LintReportPath / JsonOutputOverride; + } else { + FullOutputPath = JsonOutputOverride; + } + } + + FullOutputPath = FPaths::ConvertRelativePathToFull(FullOutputPath); + IFileManager::Get().MakeDirectory(*FPaths::GetPath(FullOutputPath), true); + UE_LOG(LinterCommandlet, Display, TEXT("Exporting JSON report to %s"), *FullOutputPath); + + const FString ReportString = LintResults->GenerateJsonReportString(); + if (FFileHelper::SaveStringToFile(ReportString, *FullOutputPath)) { + UE_LOG(LinterCommandlet, Display, TEXT("Exported JSON report successfully.")); + } else { + UE_LOG(LinterCommandlet, Error, TEXT("Failed to export JSON report. Aborting. Returning error code 1.")); + return 1; + } + } + + // write HTML report if requested + if (Switches.Contains(TEXT("html")) || ParamsMap.Contains(FString(TEXT("html")))) { + FDateTime Now = FDateTime::Now(); + FString HtmlOutputName = TEXT("lint-report-") + Now.ToString() + TEXT(".html"); + + const FString LintReportPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); + FString FullOutputPath = LintReportPath / HtmlOutputName; + + if (ParamsMap.Contains(FString(TEXT("html")))) { + const FString HtmlOutputOverride = *ParamsMap.FindChecked(TEXT("html")); + if (FPaths::IsRelative(HtmlOutputName)) { + HtmlOutputName = HtmlOutputOverride; + FullOutputPath = LintReportPath / HtmlOutputName; + } else { + FullOutputPath = HtmlOutputOverride; + } + } + + FullOutputPath = FPaths::ConvertRelativePathToFull(FullOutputPath); + IFileManager::Get().MakeDirectory(*FPaths::GetPath(FullOutputPath), true); + UE_LOG(LinterCommandlet, Display, TEXT("Exporting HTML report to %s"), *FullOutputPath); + + const FString HTMLReport = LintResults->GenerateHTML(); + if (FFileHelper::SaveStringToFile(HTMLReport, *FullOutputPath)) { + UE_LOG(LinterCommandlet, Display, TEXT("Exported HTML report successfully.")); + } else { + UE_LOG(LinterCommandlet, Error, TEXT("Failed to export HTML report. Aborting. Returning error code 1.")); + return 1; + } + } + } + + if (LintResults->Errors > 0 || (Switches.Contains(TEXT("TreatWarningsAsErrors")) && LintResults->Warnings > 0)) { + UE_LOG(LinterCommandlet, Display, TEXT("Lint completed with errors. Returning error code 2.")); + return 2; + } + + return 0; +} diff --git a/Source/Linter/Private/LinterContentBrowserExtensions.cpp b/Source/Linter/Private/LinterContentBrowserExtensions.cpp new file mode 100644 index 0000000..340846d --- /dev/null +++ b/Source/Linter/Private/LinterContentBrowserExtensions.cpp @@ -0,0 +1,134 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#include "LinterContentBrowserExtensions.h" +#include "ContentBrowserMenuContexts.h" +#include "LinterStyle.h" +#include "ContentBrowserModule.h" +#include "Linter.h" +#include "BatchRenameTool/BatchRenameTool.h" +#include "TooltipEditor/TooltipTool.h" +#include "Misc/EngineVersionComparison.h" + + +#define LOCTEXT_NAMESPACE "Linter" + +DEFINE_LOG_CATEGORY_STATIC(LinterContentBrowserExtensions, Log, All); + + +namespace { + +void RunLinterForAssets(const TArray& SelectedPaths) { + // Set Paths to Linter + if (FLinterModule* Linter = FModuleManager::GetModulePtr("Linter")) { + Linter->SetDesiredLintPaths(SelectedPaths); + } + + // Execute Linter +#if UE_VERSION_NEWER_THAN(4, 26, 0) + FGlobalTabmanager::Get()->TryInvokeTab(FName("LinterTab")); +#else + FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab")); +#endif +} + +void EditBlueprintTooltips(const TArray SelectedAssets) { + UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Opening Tooltip Tool window.")); + FTooltipTool{SelectedAssets}.ShowModal(); +} + +void BatchRenameAssets(const TArray SelectedAssets) { + UE_LOG(LinterContentBrowserExtensions, Display, TEXT("Starting batch rename.")); + FDlgBatchRenameTool{SelectedAssets}.ShowModal(); +} + +// Asset Context Menu is dynamic -> some options will only be displayed if +// you have certain types of Blueprints or Assets selected +void CreateDynamicAssetSelectionMenu(UToolMenu* InMenu) { + const auto* Context = InMenu->FindContext(); + FToolMenuSection& Section = InMenu->AddSection("Linter", LOCTEXT("LinterSection", "Linter")); + + // Convert BrowserItems to their AssetData + TArray Assets; + for (const auto& Asset : Context->SelectedItems) { + FAssetData AssetData; + Asset.Legacy_TryGetAssetData(AssetData); + Assets.Add(AssetData); + } + + // Run through the assets to determine if any are blueprints or can be renamed + bool bAnyBlueprintsSelected = false; + bool bAnyAssetCanBeRenamed = false; + + for (const auto& Asset : Assets) { + // Cannot rename redirectors or classes or cooked packages +#if UE_VERSION_NEWER_THAN(5, 1, 0) + if (!Asset.IsRedirector() && Asset.AssetClassPath.GetAssetName() != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly)) +#else + if (!Asset.IsRedirector() && Asset.AssetClass != NAME_Class && !(Asset.PackageFlags & PKG_FilterEditorOnly)) +#endif + { + bAnyAssetCanBeRenamed = true; + + if (Asset.GetClass()->IsChildOf(UBlueprint::StaticClass())) { + bAnyBlueprintsSelected = true; + break; + } + } + } + + // If we have blueprints selected, add Tooltip Editor + if (bAnyBlueprintsSelected) { + Section.AddMenuEntry( + "EditBlueprintTooltips", + LOCTEXT("EditBlueprintTooltips", "Edit Blueprint Tooltips (Experimental)"), + LOCTEXT("EditBlueprintTooltips_Tooltip", "Edit selected blueprints' templates definitions"), + FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"), + FExecuteAction::CreateStatic(&EditBlueprintTooltips, Assets) + ); + } + + // If blueprints can be renamed, add the batch rename option + if (bAnyAssetCanBeRenamed) { + Section.AddMenuEntry( + "BatchRename", + LOCTEXT("BatchRenameAssets", "Batch Rename Assets (Experimental)"), + LOCTEXT("BatchRenameAssets_Tooltip", "Perform a bulk rename operation on all of the selected assets"), + FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"), + FExecuteAction::CreateStatic(&BatchRenameAssets, Assets) + ); + } +} + +} + + +void FLinterContentBrowserExtensions::InstallHooks() { + // Run Linter on Selected Folder(s) + UToolMenu* ContentBrowserMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.FolderContextMenu"); + ContentBrowserMenu->AddSection("Linter", LOCTEXT("LinterSection", "Linter")) + .AddMenuEntry( + "LintAssets", + LOCTEXT("ScanWithLinter", "Scan with Linter"), + LOCTEXT("ScanWithLinter_Tooltip", "Scan project content with Linter"), + FSlateIcon(FLinterStyle::GetStyleSetName(), "Linter.Toolbar.Icon"), + FToolMenuExecuteAction::CreateLambda([](const FToolMenuContext& InContext) { + if (const auto* Context = InContext.FindContext()) { + RunLinterForAssets(Context->SelectedPackagePaths); + } + }) + ); + + // BatchRename and TooltipEditor + UToolMenu* AssetMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu"); + AssetMenu->AddDynamicSection("Linter", FNewToolMenuDelegate::CreateStatic(&CreateDynamicAssetSelectionMenu), {"AssetContextExploreMenuOptions", EToolMenuInsertType::After}); +} + +void FLinterContentBrowserExtensions::RemoveHooks() { + UToolMenu* ContentBrowserMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.FolderContextMenu"); + ContentBrowserMenu->RemoveSection("Linter"); + + UToolMenu* AssetMenu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu"); + AssetMenu->RemoveSection("Linter"); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/LinterNamingConvention.cpp b/Source/Linter/Private/LinterNamingConvention.cpp new file mode 100644 index 0000000..933aaec --- /dev/null +++ b/Source/Linter/Private/LinterNamingConvention.cpp @@ -0,0 +1,155 @@ +#include "LinterNamingConvention.h" + +#include "AnyObject_LinterDummyClass.h" +#include "DetailCategoryBuilder.h" +#include "DetailLayoutBuilder.h" +#include "IDetailChildrenBuilder.h" +#include "PropertyCustomizationHelpers.h" +#include "Misc/EngineVersionComparison.h" +#include "Templates/SharedPointer.h" +#if UE_VERSION_NEWER_THAN(5, 0, 0) +#include "UObject/ObjectSaveContext.h" +#endif + +TSharedRef FLinterNamingConventionDetails::MakeInstance() { + return MakeShareable(new FLinterNamingConventionDetails()); +} + +void FLinterNamingConventionDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) { + // Edit the Conventions category + IDetailCategoryBuilder& DetailCategory = DetailBuilder.EditCategory("Conventions"); + const TSharedRef NamingConventionsProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULinterNamingConvention, ClassNamingConventions), ULinterNamingConvention::StaticClass()); + + const TSharedRef ConventionsPropertyBuilder = MakeShareable(new FDetailArrayBuilder(NamingConventionsProperty)); + ConventionsPropertyBuilder->OnGenerateArrayElementWidget(FOnGenerateArrayElementWidget::CreateSP(this, &FLinterNamingConventionDetails::OnGenerateElementForDetails, &DetailBuilder)); + + + DetailCategory.AddCustomBuilder(ConventionsPropertyBuilder); +} + +void FLinterNamingConventionDetails::OnGenerateElementForDetails(const TSharedRef StructProperty, int32 ElementIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout) { + const TSharedRef RemoveButton = PropertyCustomizationHelpers::MakeRemoveButton(FSimpleDelegate::CreateLambda([this, DetailLayout, ElementIndex] { + const TSharedRef NamingConventionsProperty + = DetailLayout->GetProperty(GET_MEMBER_NAME_CHECKED(ULinterNamingConvention, ClassNamingConventions), ULinterNamingConvention::StaticClass()); + const TSharedPtr NamingConventionsPropertyHandle = NamingConventionsProperty->AsArray(); + NamingConventionsPropertyHandle->DeleteItem(ElementIndex); + } + )); + + // clang-format off + // @formatter:off + ChildrenBuilder.AddCustomRow(FText::GetEmpty()) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + RemoveButton + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + [ + SNew(SProperty, StructProperty->GetChildHandle("SoftClassPtr")) + .ShouldDisplayName(false) + ] + + SHorizontalBox::Slot() + .FillWidth(0.25f) + [ + SNew(SProperty, StructProperty->GetChildHandle("Variant")) + ] + + SHorizontalBox::Slot() + .FillWidth(0.25f) + [ + SNew(SProperty, StructProperty->GetChildHandle("Prefix")) + ] + + SHorizontalBox::Slot() + .FillWidth(0.25f) + [ + SNew(SProperty, StructProperty->GetChildHandle("Suffix")) + ] + ]; + // clang-format on + // @formatter:on +} + +ULinterNamingConvention::ULinterNamingConvention(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + ClassNamingConventions = TArray(); +} + +TArray ULinterNamingConvention::GetNamingConventionsForClassVariant(const TSoftClassPtr Class, FName Variant /*= NAME_None*/) const { + TArray NamingConventionList; + + UClass* SearchClass = Class.Get(); + while (NamingConventionList.Num() == 0 && SearchClass != nullptr) { + NamingConventionList = ClassNamingConventions.FilterByPredicate([SearchClass, Variant](const FLinterNamingConventionInfo& Info) { + return (Info.SoftClassPtr.Get() == SearchClass && Info.Variant == Variant); + }); + + // Abort if we try to go above UObject + if (SearchClass == UObject::StaticClass()) { + break; + } + + // @HACK: Editor UI won't allow us to select the UObject class in some cases + if (SearchClass == UAnyObject_LinterDummyClass::StaticClass()) { + SearchClass = UObject::StaticClass(); + continue; + } + + // Load our parent class in case we failed to get naming conventions + SearchClass = SearchClass->GetSuperClass(); + } + + return NamingConventionList; +} + +void ULinterNamingConvention::SortConventions() { + ClassNamingConventions.Sort([](const FLinterNamingConventionInfo& A, const FLinterNamingConventionInfo& B) { + if (A.SoftClassPtr.GetAssetName() < B.SoftClassPtr.GetAssetName()) { + return true; + } + + if (A.SoftClassPtr.GetAssetName() == B.SoftClassPtr.GetAssetName()) { + int32 Sort = A.Variant.Compare(B.Variant); + if (Sort < 0) { + return true; + } + + if (Sort == 0) { + Sort = A.Prefix.Compare(B.Prefix); + if (Sort < 0) { + return true; + } + + if (Sort == 0) { + Sort = A.Suffix.Compare(B.Suffix); + if (Sort <= 0) { + return true; + } + return false; + } + + return false; + } + + return false; + } + + return false; + }); +} + +#if UE_VERSION_NEWER_THAN(5, 0, 0) +void ULinterNamingConvention::PreSave(FObjectPreSaveContext ObjectSaveContext) { + Super::PreSave(ObjectSaveContext); + + SortConventions(); +} +#else +void ULinterNamingConvention::PreSave(const class ITargetPlatform* TargetPlatform) { + Super::PreSave(TargetPlatform); + + SortConventions(); +} +#endif diff --git a/Source/Linter/Private/LinterSettings.cpp b/Source/Linter/Private/LinterSettings.cpp new file mode 100644 index 0000000..c32e19c --- /dev/null +++ b/Source/Linter/Private/LinterSettings.cpp @@ -0,0 +1,14 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#include "LinterSettings.h" +#include "UObject/ConstructorHelpers.h" +#include "LintRuleSet.h" + + +ULinterSettings::ULinterSettings(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { + if (DefaultLintRuleSet.IsNull()) { + static ConstructorHelpers::FObjectFinder DefaultMarketplaceRuleSetRef(TEXT("LintRuleSet'/Linter/MarketplaceLinter/MarketplaceLintRuleSet.MarketplaceLintRuleSet'")); + DefaultLintRuleSet = DefaultMarketplaceRuleSetRef.Object; + } +} diff --git a/Source/Linter/Private/LinterStyle.cpp b/Source/Linter/Private/LinterStyle.cpp new file mode 100644 index 0000000..fe996ed --- /dev/null +++ b/Source/Linter/Private/LinterStyle.cpp @@ -0,0 +1,126 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#include "LinterStyle.h" +#include "Styling/SlateTypes.h" +#include "Styling/SlateStyle.h" +#include "Interfaces/IPluginManager.h" +#include "Styling/SlateStyleRegistry.h" +#include "Misc/EngineVersionComparison.h" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +#define IMAGE_PLUGIN_BRUSH( RelativePath, ... ) FSlateImageBrush( FLinterStyle::InContent( RelativePath, ".png" ), __VA_ARGS__ ) +#define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) +#define BOX_BRUSH(RelativePath, ...) FSlateBoxBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) +#define TTF_FONT(RelativePath, ...) FSlateFontInfo(StyleSet->RootToContentDir(RelativePath, TEXT(".ttf")), __VA_ARGS__) +#define TTF_CORE_FONT(RelativePath, ...) FSlateFontInfo(StyleSet->RootToCoreContentDir(RelativePath, TEXT(".ttf") ), __VA_ARGS__) + +FString FLinterStyle::InContent(const FString& RelativePath, const ANSICHAR* Extension) { + static FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("Linter"))->GetContentDir(); + return (ContentDir / RelativePath) + Extension; +} + +TSharedPtr FLinterStyle::StyleSet = nullptr; + +TSharedPtr FLinterStyle::Get() { + return StyleSet; +} + +FName FLinterStyle::GetStyleSetName() { + static FName LinterStyleName(TEXT("LinterStyle")); + return LinterStyleName; +} + +void FLinterStyle::Initialize() { + // Const icon sizes + const FVector2D Icon8x8(8.0f, 8.0f); + const FVector2D Icon14x14(14.0f, 14.0f); + const FVector2D Icon16x16(16.0f, 16.0f); + const FVector2D Icon20x20(20.0f, 20.0f); + const FVector2D Icon40x40(40.0f, 40.0f); + const FVector2D Icon64x64(64.0f, 64.0f); + const FVector2D Icon128x128(128.0f, 128.0f); + + // Only register once + if (StyleSet.IsValid()) { + return; + } + + StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName())); + StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate")); + StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); + + // Asset actions + { + StyleSet->Set("AssetActions.RunLinter", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); + StyleSet->Set("AssetActions.BatchRename", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); + StyleSet->Set("AssetActions.TooltipTool", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); + + // Toolbar Button Icons + StyleSet->Set("Linter.Toolbar.Icon", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); + + // Report Images + StyleSet->Set("Linter.Step.Unknown", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Unknown_64px", Icon64x64)); + StyleSet->Set("Linter.Step.Error", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Fail_64px", Icon64x64)); + StyleSet->Set("Linter.Step.Good", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Good_64px", Icon64x64)); + StyleSet->Set("Linter.Step.Working", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Working_64px", Icon64x64)); + StyleSet->Set("Linter.Step.Warning", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Warning_64px", Icon64x64)); + + StyleSet->Set("Linter.Report.Link", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Help_Documentation_16x"), Icon16x16)); + StyleSet->Set("Linter.Report.Warning", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Warning_14x"), Icon14x14)); + StyleSet->Set("Linter.Report.Error", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Error_14x"), Icon14x14)); + + StyleSet->Set("Linter.Report.Info", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Info_14x"), Icon20x20)); + + + StyleSet->Set("Linter.Step.BuildLighting.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MapCheck_64x"), Icon64x64)); + StyleSet->Set("Linter.Step.Package.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_AddContent_64x"), Icon64x64)); + StyleSet->Set("Linter.Step.SaveAll.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_file_saveall_64x"), Icon64x64)); + StyleSet->Set("Linter.Step.FixupRedirects.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_tab_Toolbars_64x"), Icon64x64)); + + // PaCK Sizing + StyleSet->Set("Linter.Padding", 2.0f); + + // PaCK Fonts + const FTextBlockStyle NormalText = FAppStyle::GetWidgetStyle("NormalText"); + + FTextBlockStyle NameText = FTextBlockStyle(NormalText) + .SetColorAndOpacity(FLinearColor(0.9f, 0.9f, 0.9f)); + { + NameText.Font.Size = 14; + StyleSet->Set("Linter.Report.AssetName", NameText); + } + + FTextBlockStyle RuleTitleText = FTextBlockStyle(NormalText) + .SetColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f)); + { + RuleTitleText.Font.Size = 12; + StyleSet->Set("Linter.Report.RuleTitle", RuleTitleText); + } + + FTextBlockStyle DescriptionText = FTextBlockStyle(NormalText) + .SetColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f)); + { + DescriptionText.Font.Size = 10; + StyleSet->Set("Linter.Report.DescriptionText", DescriptionText); + } + } + + FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); +}; + +#undef IMAGE_PLUGIN_BRUSH +#undef IMAGE_BRUSH +#undef BOX_BRUSH +#undef TTF_FONT +#undef TTF_CORE_FONT + +void FLinterStyle::Shutdown() { + if (StyleSet.IsValid()) { + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); + ensure(StyleSet.IsUnique()); + StyleSet.Reset(); + } +} diff --git a/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp b/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp new file mode 100644 index 0000000..507c201 --- /dev/null +++ b/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp @@ -0,0 +1,155 @@ +#include "TooltipEditor/TooltipStringHelper.h" + +FText FTooltipStringHelper::ParseFunctionRawTooltipGetDescription(FString RawTooltip, bool bRemoveNewlines/* = false*/) { + if (RawTooltip.Len() == 0) { + return FText::GetEmpty(); + } + + TArray Lines; + RawTooltip.ParseIntoArrayLines(Lines); + + FString Description; + + for (FString Line : Lines) { + if (Line.StartsWith(TEXT("@param"))) { + break; + } + if (Line.StartsWith(TEXT("@return"))) //@return is assumed to always be last + { + break; + } + if (Description.Len() > 0) { + Description.Append(TEXT("\n")); + } + Description.Append(Line); + } + + if (bRemoveNewlines) { + Description.ReplaceInline(TEXT("\r"), TEXT("")); + Description.ReplaceInline(TEXT("\n"), TEXT(" ")); + } + + return FText::FromString(Description); +} + +bool FTooltipStringHelper::ParseFunctionRawTooltip(FString RawTooltip, FText& OutFunctionDescription, TArray>& Inputs, TArray>& Outputs, FText& OutReturnText) { + if (RawTooltip.Len() == 0) { + return false; + } + + TArray Lines; + RawTooltip.ParseIntoArrayLines(Lines); + + FString Description; + bool bParsingDescription = true; + + FText CurrentArgumentName; + FString CurrentArgumentTooltip; + bool bCurrentArgumentIsInput = true; + + for (FString Line : Lines) { + if (Line.StartsWith(TEXT("@param"))) { + if (!bParsingDescription) { + if (bCurrentArgumentIsInput) { + FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); + } else { + FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); + } + + CurrentArgumentName = FText::GetEmpty(); + CurrentArgumentTooltip.Empty(); + } + + bParsingDescription = false; + bCurrentArgumentIsInput = true; + + Line = Line.RightChop(6); + Line.TrimStartAndEndInline(); + + if (Line.StartsWith("[out]")) { + bCurrentArgumentIsInput = false; + Line = Line.RightChop(5); + Line.TrimStartAndEndInline(); + } + + Line = Line.ConvertTabsToSpaces(4); + FString ArgumentName; + FString ArgumentTooltip; + if (Line.Split(TEXT(" "), &ArgumentName, &CurrentArgumentTooltip)) { + CurrentArgumentName = FText::FromString(ArgumentName); + CurrentArgumentTooltip.TrimStartAndEndInline(); + } + } else if (Line.StartsWith(TEXT("@return"))) //@return is assumed to always be last + { + if (!bParsingDescription) { + if (bCurrentArgumentIsInput) { + FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); + } else { + FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); + } + + CurrentArgumentName = FText::GetEmpty(); + CurrentArgumentTooltip.Empty(); + } + + Line = Line.RightChop(7); + Line.TrimStartAndEndInline(); + OutReturnText = FText::FromString(Line); + } else { + if (bParsingDescription) { + if (Description.Len() > 0) { + Description.Append(TEXT("\n")); + } + Description.Append(Line); + } else { + Line.TrimStartAndEndInline(); + CurrentArgumentTooltip.AppendChar(TEXT(' ')); + CurrentArgumentTooltip.Append(Line); + } + } + } + + if (!CurrentArgumentName.IsEmpty()) { + if (!bParsingDescription) { + if (bCurrentArgumentIsInput) { + FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); + } else { + FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); + } + + CurrentArgumentName = FText::GetEmpty(); + CurrentArgumentTooltip.Empty(); + } + } + + OutFunctionDescription = FText::FromString(Description); + return true; +} + +FString FTooltipStringHelper::ConvertTooltipDataToRawTooltip(FText FunctionDescription, TArray> Inputs, TArray> Outputs) { + FString RawTooltip = FunctionDescription.ToString(); + for (const auto& Arg : Inputs) { + RawTooltip.Append(FString::Printf(TEXT("\n@param %s %s\t\t\t%s"), TEXT(" "), *Arg->ArgumentName.ToString(), *Arg->Tooltip.ToString())); + } + + if (Outputs.Num() == 1) { + RawTooltip.Append(FString::Printf(TEXT("\n@return %s"), *Outputs[0]->Tooltip.ToString())); + } else { + for (const auto& Arg : Outputs) { + RawTooltip.Append(FString::Printf(TEXT("\n@param %s %s\t\t\t%s"), TEXT("[out]"), *Arg->ArgumentName.ToString(), *Arg->Tooltip.ToString())); + } + } + + return RawTooltip; +} + +bool FTooltipStringHelper::FindAndUpdateArgumentTooltip(FText ArgumentName, FText Tooltip, TArray>& Arguments) { + const TSharedPtr* FuncArg = Arguments.FindByPredicate([&](TSharedPtr Arg) { + return Arg->ArgumentName.EqualTo(ArgumentName, ETextComparisonLevel::Quinary); + }); + if (FuncArg != nullptr) { + (*FuncArg)->Tooltip = Tooltip; + return true; + } + return false; +} diff --git a/Source/Linter/Private/TooltipTool/TooltipTool.cpp b/Source/Linter/Private/TooltipTool/TooltipTool.cpp new file mode 100644 index 0000000..e3c2a5d --- /dev/null +++ b/Source/Linter/Private/TooltipTool/TooltipTool.cpp @@ -0,0 +1,735 @@ +// Copyright 2016 Gamemakin LLC. All Rights Reserved. + +#include "TooltipEditor/TooltipTool.h" + +#include "Framework/Application/SlateApplication.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/Layout/SHeader.h" +#include "Widgets/Images/SImage.h" +#include "Framework/Text/TextLayout.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Layout/SScrollBox.h" +#include "Widgets/Layout/SExpandableArea.h" +#include "Widgets/Input/SButton.h" +#include "Types/SlateEnums.h" +#include "Editor.h" +#include "EdGraphSchema_K2.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "FileHelpers.h" +#include "Widgets/Layout/SSeparator.h" +#include "Misc/EngineVersionComparison.h" + +#define LOCTEXT_NAMESPACE "LinterTooltipTool" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +FTooltipTool::FTooltipTool(const TArray Assets) { + for (auto Asset : Assets) { + if (Asset.GetClass()->IsChildOf(UBlueprint::StaticClass())) { + BlueprintsInternal.Push(Asset); + } + } + + for (int32 i = 0; i < BlueprintsInternal.Num(); ++i) { + Blueprints.Push(TSharedPtr(&BlueprintsInternal[i])); + } + + if (FSlateApplication::IsInitialized()) { + // clang-format off + // @formatter:off + DialogWindow = SNew(SWindow) + .Title(LOCTEXT("TooltipToolDlgTitle", "Blueprint Member Tooltip Tool")) + .SupportsMinimize(false).SupportsMaximize(false) + .SaneWindowPlacement(true) + .AutoCenter(EAutoCenter::PreferredWorkArea) + .MinWidth(400.0f) + .MaxWidth(400.0f) + .SizingRule(ESizingRule::Autosized); + + const TSharedPtr DialogWrapper = + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(4.0f) + [ + SAssignNew(DialogWidget, STooltipTool) + .ParentWindow(DialogWindow) + .Blueprints(Blueprints) + ]; + // clang-format on + // @formatter:on + + DialogWindow->SetContent(DialogWrapper.ToSharedRef()); + } +} + +FTooltipTool::EResult FTooltipTool::ShowModal() { + //Show Dialog + GEditor->EditorAddModalWindow(DialogWindow.ToSharedRef()); + const EResult UserResponse = DialogWidget->GetUserResponse(); + + if (UserResponse == Confirm) { } + return UserResponse; +} + +void STooltipTool::Construct(const FArguments& InArgs) { + UserResponse = FTooltipTool::Cancel; + ParentWindow = InArgs._ParentWindow.Get(); + Blueprints = InArgs._Blueprints; + check(Blueprints.IsSet() && Blueprints.Get().Num() > 0); + + // clang-format off + // @formatter:off + this->ChildSlot[ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 0.0f) + [ + SNew(SHeader) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolBlueprintHeader", "Blueprint")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SAssignNew(BlueprintComboBox, SComboBox>) + .OptionsSource(&Blueprints.Get()) + .InitiallySelectedItem(Blueprints.Get()[0]) + .OnGenerateWidget_Lambda([](const TSharedPtr Item) { + return + SNew(STextBlock) + .Text(FText::FromName(Item->AssetName)) + .ToolTipText(FText::FromString(Item->GetFullName())); + }) + .OnSelectionChanged_Lambda([&](TSharedPtr Item, ESelectInfo::Type SelectInfo) { + VariableTooltipEditableTextBox->SetEnabled(false); + RebuildMemberList(); + }) + [ + SNew(STextBlock) + .Text(this, &STooltipTool::GetSelectedBlueprintText) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .FillWidth(1.0f) + [ + SNew(SButton) + .OnClicked_Lambda([&]() { + int32 Index = Blueprints.Get().Find(BlueprintComboBox->GetSelectedItem()); + Index = (Blueprints.Get().Num() + Index - 1) % Blueprints.Get().Num(); + BlueprintComboBox->SetSelectedItem(Blueprints.Get()[Index]); + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolBlueprintsPrevious", "Previous")) + ] + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + [ + SNew(SButton) + .OnClicked_Lambda([&]() { + int32 Index = Blueprints.Get().Find(BlueprintComboBox->GetSelectedItem()); + Index = (Index + 1) % Blueprints.Get().Num(); + BlueprintComboBox->SetSelectedItem(Blueprints.Get()[Index]); + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolBlueprintsNext", "Next")) + ] + ] + + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SButton) + .OnClicked_Lambda([&]() { + // Save package here if SCC is enabled because the user can use SCC to revert a change + TArray OutermostPackagesToSave; + for (auto&& Asset : Blueprints.Get()) { + OutermostPackagesToSave.Add(Asset->GetPackage()); + } + FEditorFileUtils::PromptForCheckoutAndSave(OutermostPackagesToSave, true, false); + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolBlueprintsSaveButtonLabel", "Save All Blueprints")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SExpandableArea) + .InitiallyCollapsed(true) + .AreaTitle(LOCTEXT("TooltipToolVariablesExpandableTitle", "Variables")) + .BodyContent() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SBox) + .HeightOverride(100.0f) + [ + SNew(SBorder) + .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) + .Padding(0.0f) + [ + SNew(SScrollBox) + .ScrollBarAlwaysVisible(true) + + SScrollBox::Slot() + [ + SAssignNew(MemberListView, SListView>) + .SelectionMode(ESelectionMode::Single) + .ListItemsSource(&Members) + .OnGenerateRow_Lambda([](TSharedPtr Item, const TSharedRef& OwnerTable) { + return + SNew(STableRow>, OwnerTable) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SImage) + .Image(FAppStyle::GetBrush(TEXT("Icons.Error"))) + .Visibility_Lambda([Item] { + return Item.IsValid() ? (Item->HasMetaData(FBlueprintMetadata::MD_Tooltip) && Item->GetMetaData(FBlueprintMetadata::MD_Tooltip).Len() > 0 ? EVisibility::Collapsed : EVisibility::HitTestInvisible) : EVisibility::Collapsed; + }) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(FText::FromString(Item->FriendlyName)) + ] + + SHorizontalBox::Slot() + .FillWidth(1.0f) + [ + SNew(STextBlock) + .Text(UEdGraphSchema_K2::TypeToText(Item->VarType)) + .Justification(ETextJustify::Right) + ] + ]; + }) + .OnSelectionChanged_Lambda([&](const TSharedPtr Item, ESelectInfo::Type SelectInfo) { + if (!Item.IsValid()) { + VariableTooltipEditableTextBox->SetEnabled(false); + VariableTooltipEditableTextBox->SetText(FText::GetEmpty()); + return; + } + + VariableTooltipEditableTextBox->SetEnabled(true); + if (Item->HasMetaData(FBlueprintMetadata::MD_Tooltip)) { + VariableTooltipEditableTextBox->SetText(FText::FromString(Item->GetMetaData(FBlueprintMetadata::MD_Tooltip))); + } else { + VariableTooltipEditableTextBox->SetText(FText::GetEmpty()); + } + }) + ] + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SHeader) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolTooltipHeader", "Tooltip")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SBox) + .HeightOverride(100.0f) + [ + SAssignNew(VariableTooltipEditableTextBox, SMultiLineEditableTextBox) + .AutoWrapText(true) + .IsEnabled(false) + .OnTextChanged_Lambda([&](const FText& NewText) { + if (CommitOnTextChangeCheckBox->IsChecked()) { + UpdateVariableTooltipText(NewText); + } + }) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SAssignNew(CommitTextButton, SButton) + .OnClicked_Lambda([&]() { + /**UpdateVariableTooltipText(TooltipEditableTextBox->GetText());**/ + return FReply::Handled(); + }) + .Visibility_Lambda([&]() { + return CommitOnTextChangeCheckBox->IsChecked() ? EVisibility::Collapsed : EVisibility::Visible; + }) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolCommitTextButtonLabel", "Commit Text")) + ] + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SExpandableArea) + .InitiallyCollapsed(true) + .AreaTitle(LOCTEXT("TooltipToolFunctionExpandableTitle", "Functions")) + .BodyContent() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SBox) + .HeightOverride(100.0f) + [ + SNew(SBorder) + .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) + .Padding(0.0f) + [ + SNew(SScrollBox) + .ScrollBarAlwaysVisible(true) + + SScrollBox::Slot() + [ + SAssignNew(FunctionListView, SListView>) + .SelectionMode(ESelectionMode::Single) + .ListItemsSource(&FunctionPointers) + .OnGenerateRow_Lambda([](TSharedPtr Item, const TSharedRef& OwnerTable) { + return + SNew(STableRow>, OwnerTable) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SImage) + .Image(FAppStyle::GetBrush(TEXT("Icons.Error"))) + .Visibility_Lambda([Item] { + return (Item.IsValid() && Item->FunctionEntryNode != nullptr && !Item->FunctionEntryNode->MetaData.ToolTip.IsEmptyOrWhitespace()) ? EVisibility::Collapsed : EVisibility::Visible; + }) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(FText::FromName(Item->FunctionName)) + ] + ]; + }) + .OnSelectionChanged_Lambda([&](const TSharedPtr Item, ESelectInfo::Type SelectInfo) { + FunctionArgumentDescriptions.Empty(); + FunctionOutputDescriptions.Empty(); + + if (!Item.IsValid()) { + FunctionArgumentListView->RebuildList(); + FunctionOutputListView->RebuildList(); + + FunctionDescriptionTooltipBox->SetEnabled(false); + FunctionArgumentListView->SetEnabled(false); + FunctionOutputListView->SetEnabled(false); + FunctionDescriptionTooltipBox->SetText(FText::GetEmpty()); + + return; + } + + check(Item->FunctionEntryNode); + TArray InputPins = Item->FunctionEntryNode->GetAllPins(); + for (const UEdGraphPin* Pin : InputPins) { + if (Pin->Direction == EGPD_Output) { + if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec) { + FunctionArgumentDescriptions.Add(MakeShared(FText::FromString(Pin->GetName()), FText::GetEmpty(), UEdGraphSchema_K2::TypeToText(Pin->PinType))); + } + } + } + + if (Item->FunctionResultNode != nullptr) { + TArray OutputPins = Item->FunctionResultNode->GetAllPins(); + for (const UEdGraphPin* Pin : OutputPins) { + if (Pin->Direction == EGPD_Input) { + if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec) { + FunctionOutputDescriptions.Add(MakeShared(FText::FromString(Pin->GetName()), FText::GetEmpty(), UEdGraphSchema_K2::TypeToText(Pin->PinType))); + } + } + } + } + + FunctionDescriptionTooltipBox->SetEnabled(true); + FunctionArgumentListView->SetEnabled(true); + FunctionOutputListView->SetEnabled(true); + + FText FunctionDescription; + FText ReturnText; + + if (!FTooltipStringHelper::ParseFunctionRawTooltip(Item->FunctionEntryNode->MetaData.ToolTip.ToString(), CurrentFunctionDescription, FunctionArgumentDescriptions, FunctionOutputDescriptions, ReturnText)) { + CurrentFunctionDescription = FText::GetEmpty(); + } + + if (!ReturnText.IsEmptyOrWhitespace() && FunctionOutputDescriptions.Num() > 0) { + FunctionOutputDescriptions[FunctionOutputDescriptions.Num() - 1]->Tooltip = ReturnText; + } + + FunctionArgumentListView->RebuildList(); + FunctionOutputListView->RebuildList(); + + FunctionDescriptionTooltipBox->SetText(CurrentFunctionDescription); + }) + ] + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHeader) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolFunctionDesc", "Description")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SBox) + .HeightOverride(60.0f) + [ + SAssignNew(FunctionDescriptionTooltipBox, SMultiLineEditableTextBox) + .AutoWrapText(true) + .IsEnabled(false) + .OnTextChanged_Lambda([&](const FText& NewText) { + if (CommitOnTextChangeCheckBox->IsChecked()) { + if (!NewText.EqualTo(CurrentFunctionDescription)) { + UpdateCurrentFunctionTooltipText(); + } + } + }) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHeader) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolFunctionInputs", "Inputs")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SBox) + .MinDesiredHeight(20.0f) + .MaxDesiredHeight(100.0f) + [ + SNew(SBorder) + .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) + .Padding(0.0f) + [ + SNew(SScrollBox) + .ScrollBarAlwaysVisible(true) + + SScrollBox::Slot() + [ + SAssignNew(FunctionArgumentListView, SListView>) + .SelectionMode(ESelectionMode::None) + .ListItemsSource(&FunctionArgumentDescriptions) + .OnGenerateRow_Lambda([&](TSharedPtr Item, const TSharedRef& OwnerTable) { + return SNew(STableRow>, OwnerTable) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SImage) + .Image(FAppStyle::GetBrush(TEXT("Icons.Error"))) + .Visibility_Lambda([Item] { + return (Item.IsValid() && !Item->Tooltip.IsEmptyOrWhitespace()) ? EVisibility::Collapsed : EVisibility::Visible; + }) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(FText::FromString(FString::Printf(TEXT("%s (%s)"), *Item->ArgumentName.ToString(), *Item->ArgumentType.ToString()))) + ] + ] + + SVerticalBox::Slot() + .Padding(0.0f, 4.0f, 0.0f, 4.0f) + [ + SNew(SEditableTextBox) + .Text(Item->Tooltip) + .OnTextChanged_Lambda([&](const FText& NewText) { + if (CommitOnTextChangeCheckBox->IsChecked()) { + UpdateCurrentFunctionTooltipText(); + } + }) + ] + ]; + }) + ] + ] + ] + ] + + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHeader) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolFunctionOutputs", "Outputs")) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SBox) + .MinDesiredHeight(20.0f) + .MaxDesiredHeight(100.0f) + [ + SNew(SBorder) + .BorderImage(FCoreStyle::Get().GetBrush("ToolPanel.GroupBorder")) + .Padding(0.0f) + [ + SNew(SScrollBox) + .ScrollBarAlwaysVisible(true) + + SScrollBox::Slot() + [ + SAssignNew(FunctionOutputListView, SListView>) + .SelectionMode(ESelectionMode::None) + .ListItemsSource(&FunctionOutputDescriptions) + .OnGenerateRow_Lambda([&](TSharedPtr Item, const TSharedRef& OwnerTable) { + return SNew(STableRow>, OwnerTable) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SImage) + .Image(FAppStyle::GetBrush(TEXT("Icons.Error"))) + .Visibility_Lambda([Item] { + return (Item.IsValid() && !Item->Tooltip.IsEmptyOrWhitespace()) ? EVisibility::Collapsed : EVisibility::Visible; + }) + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(FText::FromString(FString::Printf(TEXT("%s (%s)"), *Item->ArgumentName.ToString(), *Item->ArgumentType.ToString()))) + ] + ] + + SVerticalBox::Slot() + .Padding(0.0f, 4.0f, 0.0f, 4.0f) + [ + SNew(SEditableTextBox) + .Text(Item->Tooltip) + .OnTextChanged_Lambda([&](const FText& NewText) { + if (CommitOnTextChangeCheckBox->IsChecked()) { + UpdateCurrentFunctionTooltipText(); + } + }) + ] + ]; + }) + ] + ] + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SAssignNew(CommitTextButton, SButton) + .OnClicked_Lambda([&] { + UpdateVariableTooltipText(VariableTooltipEditableTextBox->GetText()); + return FReply::Handled(); + }) + .Visibility_Lambda([&] { + return CommitOnTextChangeCheckBox->IsChecked() ? EVisibility::Collapsed : EVisibility::Visible; + }) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolCommitTextButtonLabel", "Commit Text")) + ] + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 4.0f) + [ + SNew(SSeparator) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(8.0f, 4.0f, 8.0f, 0.0f) + [ + SNew(SExpandableArea) + .InitiallyCollapsed(true) + .AreaTitle(LOCTEXT("TooltipToolBehaviorExpandableTitle", "Tool Behavior")) + .BodyContent() + [ + SAssignNew(CommitOnTextChangeCheckBox, SCheckBox) + .IsChecked(ECheckBoxState::Checked) + [ + SNew(STextBlock) + .Text(LOCTEXT("TooltipToolCommitOnChangeLabel", "Commit Tooltip on Text Change")) + ] + ] + ] + ]; + // clang-format on + // @formatter:on + + RebuildMemberList(); +} + +FTooltipTool::EResult STooltipTool::GetUserResponse() const { + return UserResponse; +} + +FReply STooltipTool::OnButtonClick(const FTooltipTool::EResult ButtonID) { + ParentWindow->RequestDestroyWindow(); + UserResponse = ButtonID; + + return FReply::Handled(); +} + +FText STooltipTool::GetSelectedBlueprintText() const { + if (BlueprintComboBox->GetSelectedItem().IsValid()) { + return FText::FromName(BlueprintComboBox->GetSelectedItem().Get()->AssetName); + } + + return FText::FromString(TEXT("No Blueprint Selected.")); +} + +void STooltipTool::UpdateVariableTooltipText(const FText& NewText) { + // Early out if text box isn't enabled + if (!VariableTooltipEditableTextBox->IsEnabled()) { + return; + } + + TArray> SelectedMembers; + MemberListView->GetSelectedItems(SelectedMembers); + if (SelectedMembers.Num() == 1) { + const TSharedPtr BlueprintAsset = BlueprintComboBox->GetSelectedItem(); + UBlueprint* Blueprint = CastChecked(BlueprintAsset->GetAsset()); + SelectedMembers[0]->SetMetaData(FBlueprintMetadata::MD_Tooltip, NewText.ToString()); + FBlueprintEditorUtils::SetBlueprintVariableMetaData(Blueprint, SelectedMembers[0]->VarName, nullptr, FBlueprintMetadata::MD_Tooltip, NewText.ToString()); + } else { + check(false); + } +} + +void STooltipTool::UpdateCurrentFunctionTooltipText() { + TArray> SelectedFunctions = FunctionListView->GetSelectedItems(); + + if (SelectedFunctions.Num() == 0) { + return; + } + + check(SelectedFunctions.Num() == 1); //If anything is selected, only one thing should be selected. + + for (int32 i = 0; i < FunctionArgumentDescriptions.Num(); i++) { + // @TODO: Don't do this + TSharedRef Child = FunctionArgumentListView->WidgetFromItem(FunctionArgumentDescriptions[i])->GetContent()->GetChildren()->GetChildAt(1); + const SEditableTextBox& TooltipBox = static_cast(Child.Get()); + FunctionArgumentDescriptions[i]->Tooltip = TooltipBox.GetText(); + } + + for (int32 i = 0; i < FunctionOutputDescriptions.Num(); i++) { + TSharedRef Child = FunctionOutputListView->WidgetFromItem(FunctionOutputDescriptions[i])->GetContent()->GetChildren()->GetChildAt(1); + const SEditableTextBox& TooltipBox = static_cast(Child.Get()); + FunctionOutputDescriptions[i]->Tooltip = TooltipBox.GetText(); + } + + const FString RawTooltip = FTooltipStringHelper::ConvertTooltipDataToRawTooltip(FunctionDescriptionTooltipBox->GetText(), FunctionArgumentDescriptions, FunctionOutputDescriptions); + + const TSharedPtr FunctionPointer = SelectedFunctions[0]; + FunctionPointer->FunctionEntryNode->MetaData.ToolTip = FText::FromString(RawTooltip); +} + +void STooltipTool::RebuildMemberList() { + Members.Empty(); + FunctionPointers.Empty(); + FunctionArgumentDescriptions.Empty(); + FunctionOutputDescriptions.Empty(); + + if (!BlueprintComboBox->GetSelectedItem().IsValid()) { + return; + } + + UBlueprint* Blueprint = Cast(BlueprintComboBox->GetSelectedItem().Get()->GetAsset()); + + // Get variables + for (const FBPVariableDescription& Member : Blueprint->NewVariables) { + if ((Member.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) { + Members.Push(MakeShared(Member)); + } + } + + // Get functions + for (const UEdGraph* FunctionGraph : Blueprint->FunctionGraphs) { + if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) { + TWeakObjectPtr FuncEntryPtr; + TWeakObjectPtr FuncResultPtr; + FBlueprintEditorUtils::GetEntryAndResultNodes(FunctionGraph, FuncEntryPtr, FuncResultPtr); + + UK2Node_FunctionEntry* FunctionEntryNode = Cast(FuncEntryPtr.Get()); + UK2Node_FunctionResult* FunctionResultNode = Cast(FuncResultPtr.Get()); + + if (FunctionEntryNode != nullptr && FunctionEntryNode->IsEditable() && FunctionEntryNode->GetFunctionFlags() & FUNC_Public) { + FunctionPointers.Push(MakeShared(FunctionEntryNode, FunctionResultNode, FunctionEntryNode->GetGraph()->GetFName())); + } + } + } + + MemberListView->RebuildList(); + FunctionListView->RebuildList(); + + if (Members.Num() > 0) { + MemberListView->SetSelection(Members[0], ESelectInfo::Direct); + } + + if (FunctionPointers.Num() > 0) { + FunctionListView->SetSelection(FunctionPointers[0], ESelectInfo::Direct); + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReport.cpp b/Source/Linter/Private/UI/LintReport.cpp new file mode 100644 index 0000000..927f234 --- /dev/null +++ b/Source/Linter/Private/UI/LintReport.cpp @@ -0,0 +1,377 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "UI/LintReport.h" +#include "LintRule.h" +#include "Widgets/SBoxPanel.h" +#include "LintRuleSet.h" +#include "UI/LintReportAssetDetails.h" +#include "AssetThumbnail.h" +#include "Containers/Map.h" +#include "LinterSettings.h" +#include "Misc/ScopedSlowTask.h" +#include "Widgets/Layout/SSpacer.h" +#include "Dom/JsonObject.h" +#include "Serialization/JsonWriter.h" +#include "Policies/PrettyJsonPrintPolicy.h" +#include "Serialization/JsonSerializer.h" +#include "Dom/JsonValue.h" +#include "DesktopPlatformModule.h" +#include "IDesktopPlatform.h" +#include "Linter.h" +#include "LinterStyle.h" +#include "Interfaces/IPluginManager.h" +#include "Misc/FileHelper.h" +#include "Widgets/Input/SComboButton.h" +#include "UI/LintReportRuleDetails.h" + +#define LOCTEXT_NAMESPACE "Linter" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +void SLintReport::Construct(const FArguments& Args) { + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .VAlign(VAlign_Fill) + .AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .HAlign(HAlign_Left) + .AutoWidth() + .Padding(PaddingAmount) + [ + SNew(SButton) + .Text(LOCTEXT("Rescan", "Rescan")) + .OnClicked_Lambda([this]() -> FReply { + Rebuild(LastUsedRuleSet); + return FReply::Handled(); + }) + ] + + SHorizontalBox::Slot() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(PaddingAmount) + [ + SAssignNew(ResultsTextBlockPtr, STextBlock) + ] + + SHorizontalBox::Slot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Center) + .FillWidth(1.0f) + .Padding(PaddingAmount) + [ + SNew(SSpacer) + ] + + SHorizontalBox::Slot() + .HAlign(HAlign_Right) + .AutoWidth() + .Padding(PaddingAmount) + [ + SNew(SButton) + .Text(LOCTEXT("ExportToJSON", "Export To JSON")) + .OnClicked_Lambda([this]() -> FReply { + IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); + + const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr); + + const FText Title = LOCTEXT("ExportToJsonTitle", "Export Lint Report as JSON"); + const FString FileTypes = TEXT("Json (*.json)|*.json"); + + const FDateTime Now = FDateTime::Now(); + const FString Output = TEXT("lint-report-") + Now.ToString() + TEXT(".json"); + + FString DefaultPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); + DefaultPath = FPaths::ConvertRelativePathToFull(DefaultPath); + IFileManager::Get().MakeDirectory(*DefaultPath, true); + + TArray OutFilenames; + DesktopPlatform->SaveFileDialog( + ParentWindowWindowHandle, + Title.ToString(), + DefaultPath, + Output, + FileTypes, + EFileDialogFlags::None, + OutFilenames + ); + + if (OutFilenames.Num() > 0) { + const FString WritePath = FPaths::ConvertRelativePathToFull(OutFilenames[0]); + FFileHelper::SaveStringToFile(JsonReport, *WritePath); + FPlatformProcess::LaunchURL(*WritePath, TEXT(""), nullptr); + } + + return FReply::Handled(); + }) + ] + + SHorizontalBox::Slot() + .HAlign(HAlign_Right) + .AutoWidth() + .Padding(PaddingAmount) + [ + SNew(SButton) + .Text(LOCTEXT("ExportToHTML", "Export To HTML")) + .OnClicked_Lambda([this]() -> FReply { + IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); + + const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr); + + const FText Title = LOCTEXT("ExportToHTMLTitle", "Export Lint Report as HTML"); + const FString FileTypes = TEXT("HTML (*.html)|*.html"); + + const FDateTime Now = FDateTime::Now(); + const FString Output = TEXT("lint-report-") + Now.ToString() + TEXT(".html"); + + FString DefaultPath = FPaths::ProjectSavedDir() / TEXT("LintReports"); + DefaultPath = FPaths::ConvertRelativePathToFull(DefaultPath); + IFileManager::Get().MakeDirectory(*DefaultPath, true); + + TArray OutFilenames; + DesktopPlatform->SaveFileDialog( + ParentWindowWindowHandle, + Title.ToString(), + DefaultPath, + Output, + FileTypes, + EFileDialogFlags::None, + OutFilenames + ); + + if (OutFilenames.Num() > 0) { + const FString WritePath = FPaths::ConvertRelativePathToFull(OutFilenames[0]); + FFileHelper::SaveStringToFile(HTMLReport, *WritePath); + FPlatformProcess::LaunchURL(*WritePath, TEXT(""), nullptr); + } + + return FReply::Handled(); + }) + ] + ] + + SVerticalBox::Slot() + .VAlign(VAlign_Fill) + .FillHeight(1.0f) + .Padding(PaddingAmount) + [ + SAssignNew(AssetDetailsScrollBoxPtr, SScrollBox) + .ScrollBarAlwaysVisible(true) + ] + + SVerticalBox::Slot() + .VAlign(VAlign_Fill) + .FillHeight(1.0f) + .Padding(PaddingAmount) + [ + SAssignNew(RuleDetailsScrollBoxPtr, SScrollBox) + .ScrollBarAlwaysVisible(true) + .Visibility(EVisibility::Collapsed) + ] + // Bottom panel + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Top) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(FMargin(4.0f, 0.0f, 4.0f, 2.0f)) + // .Visibility_Lambda([&]() { return AssetErrorLists.Num() > 0 ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; }) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(FMargin(2.0f, 0.0f, 2.0f, 2.0f)) + [ + + SNew(SHorizontalBox) + // View mode combo button + + SHorizontalBox::Slot() + .FillWidth(1.f) + .VAlign(VAlign_Center) + .HAlign(HAlign_Right) + [ + SAssignNew(ViewOptionsComboButton, SComboButton) + .ContentPadding(0) + .ForegroundColor_Lambda([&]() { + return ViewOptionsComboButton->IsHovered() ? FAppStyle::GetSlateColor("InvertedForeground") : FAppStyle::GetSlateColor("DefaultForeground"); + }) + .ButtonStyle(FAppStyle::Get(), "ToggleButton") // Use the tool bar item style for this button + .OnGetMenuContent(this, &SLintReport::GetViewButtonContent) + .ButtonContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + [ + SNew(SImage) + .Image(FAppStyle::GetBrush("GenericViewButton")) + ] + + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(2, 0, 0, 0) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(LOCTEXT("ViewButton", "View Options")) + ] + ] + ] + ] + ] + ] + ]; + // clang-format on + // @formatter:on +} + +void SLintReport::Rebuild(const ULintRuleSet* SelectedLintRuleSet) { + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + bHasRanReport = false; + + if (SelectedLintRuleSet == nullptr) { + SelectedLintRuleSet = GetDefault()->DefaultLintRuleSet.LoadSynchronous(); + } + + check(SelectedLintRuleSet != nullptr); + LastUsedRuleSet = SelectedLintRuleSet; + + AssetDetailsScrollBoxPtr->ClearChildren(); + RuleDetailsScrollBoxPtr->ClearChildren(); + RuleViolations.Reset(); + + FScopedSlowTask SlowTask(0, LOCTEXT("LintingInProgress", "Linting Assets...")); + SlowTask.MakeDialog(false); + + FLinterModule& LinterModule = FModuleManager::LoadModuleChecked(TEXT("Linter")); + TArray LintPaths = LinterModule.GetDesiredLintPaths(); + + LintResults = SelectedLintRuleSet->LintPath(LintPaths, &SlowTask); + RuleViolations = LintResults->GetSharedViolations(); + + TArray UniqueViolators = FLintRuleViolation::AllRuleViolationViolators(RuleViolations); + TSharedPtr ThumbnailPool = MakeShared(UniqueViolators.Num()); + + for (const UObject* Violator : UniqueViolators) { + TArray> Violations = FLintRuleViolation::AllRuleViolationsWithViolatorShared(RuleViolations, Violator); + + FAssetData AssetData; + if (Violations.Num() > 0) { + AssetData = Violations[0]->ViolatorAssetData; + } + + // clang-format off + // @formatter:off + AssetDetailsScrollBoxPtr.Get()->AddSlot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .Padding(PaddingAmount) + [ + SNew(SLintReportAssetDetails) + .AssetData(AssetData) + .RuleViolations(Violations) + .ThumbnailPool(ThumbnailPool) + ]; + // clang-format on + // @formatter:on + } + + TMultiMap> ViolationsMappedByRule = FLintRuleViolation::AllRuleViolationsMappedByViolatedLintRuleShared(RuleViolations); + TSharedPtr RuleThumbnailPool = MakeShared(ViolationsMappedByRule.Num()); // In case we ever want to render 'rule thumbnails' in the future + + TArray UniqueRules; + ViolationsMappedByRule.GetKeys(UniqueRules); + + for (const ULintRule* BrokenRule : UniqueRules) { + TArray> ViolatorsOfBrokenRule; + ViolationsMappedByRule.MultiFind(BrokenRule, ViolatorsOfBrokenRule); + + if (ViolatorsOfBrokenRule.Num() > 0) { + // clang-format off + // @formatter:off + RuleDetailsScrollBoxPtr.Get()->AddSlot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .Padding(PaddingAmount) + [ + SNew(SLintReportRuleDetails) + .RuleViolations(ViolatorsOfBrokenRule) + .ThumbnailPool(RuleThumbnailPool) + ]; + // clang-format on + // @formatter:on + } + } + + // Update Summary Text Block + ResultsTextBlockPtr->SetText(LintResults->Result); + + // Genereate JSON and HTML reports + JsonReport = LintResults->GenerateJsonReportString(); + HTMLReport = LintResults->GenerateHTML(); + + bHasRanReport = true; +} + +TSharedRef SLintReport::GetViewButtonContent() { + FMenuBuilder MenuBuilder(/*bInShouldCloseWindowAfterMenuSelection=*/true, nullptr, TSharedPtr(), /*bCloseSelfOnly=*/ true); + + MenuBuilder.BeginSection("AssetViewType", LOCTEXT("ViewTypeHeading", "View Type")); + { + MenuBuilder.AddMenuEntry( + LOCTEXT("RuleFirstOption", "Assets by Guideline"), + LOCTEXT("RuleFirstOptionTooltip", "View failing assets as a categorized list of guidelines."), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([&]() { + if (AssetDetailsScrollBoxPtr.IsValid()) { + AssetDetailsScrollBoxPtr->SetVisibility(EVisibility::SelfHitTestInvisible); + } + if (RuleDetailsScrollBoxPtr.IsValid()) { + RuleDetailsScrollBoxPtr->SetVisibility(EVisibility::Collapsed); + } + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([&]() { + return AssetDetailsScrollBoxPtr.IsValid() && AssetDetailsScrollBoxPtr->GetVisibility().IsVisible(); + }) + ), + NAME_None, + EUserInterfaceActionType::RadioButton + ); + + MenuBuilder.AddMenuEntry( + LOCTEXT("AssetsFirstOption", "Guidelines by Asset"), + LOCTEXT("AssetsFirstOptionTooltip", "View the failing assets one at a time with all their respective errors."), + FSlateIcon(), + FUIAction( + FExecuteAction::CreateLambda([&]() { + if (AssetDetailsScrollBoxPtr.IsValid()) { + AssetDetailsScrollBoxPtr->SetVisibility(EVisibility::Collapsed); + } + if (RuleDetailsScrollBoxPtr.IsValid()) { + RuleDetailsScrollBoxPtr->SetVisibility(EVisibility::SelfHitTestInvisible); + } + }), + FCanExecuteAction(), + FIsActionChecked::CreateLambda([&]() { + return RuleDetailsScrollBoxPtr.IsValid() && RuleDetailsScrollBoxPtr->GetVisibility().IsVisible(); + }) + ), + NAME_None, + EUserInterfaceActionType::RadioButton + ); + } + + MenuBuilder.EndSection(); + + return MenuBuilder.MakeWidget(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReportAssetDetails.cpp b/Source/Linter/Private/UI/LintReportAssetDetails.cpp new file mode 100644 index 0000000..e842e4c --- /dev/null +++ b/Source/Linter/Private/UI/LintReportAssetDetails.cpp @@ -0,0 +1,146 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "UI/LintReportAssetDetails.h" +#include "LinterStyle.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Layout/SExpandableArea.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Widgets/Input/SHyperlink.h" +#include "Internationalization/Internationalization.h" +#include "Widgets/Text/STextBlock.h" +#include "LintRule.h" +#include "AssetThumbnail.h" +#include "UI/LintReportAssetErrorList.h" +#include "Misc/EngineVersionComparison.h" + + +#define LOCTEXT_NAMESPACE "LintReport" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +void SLintReportAssetDetails::Construct(const FArguments& Args) { + AssetData = Args._AssetData; + RuleViolations = Args._RuleViolations; + ThumbnailPool = Args._ThumbnailPool; + + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + const FText AssetName = FText::FromString(AssetData.Get().AssetName.ToString()); + const FText AssetPath = FText::FromString(AssetData.Get().GetFullName()); + + const TSharedPtr AssetThumbnail = MakeShareable(new FAssetThumbnail(AssetData.Get().GetAsset(), 96, 96, ThumbnailPool.Get())); + + int32 NumErrors = 0; + int32 NumWarnings = 0; + + for (const auto& RuleViolation : RuleViolations.Get()) { + switch (RuleViolation->ViolatedRule.Get()->GetDefaultObject()->RuleSeverity) { + case ELintRuleSeverity::Error: NumErrors++; + break; + case ELintRuleSeverity::Warning: NumWarnings++; + break; + case ELintRuleSeverity::Info: break; + //case ELintRuleSeverity::Ignore: + default: break; + } + } + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(PaddingAmount) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(PaddingAmount) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SExpandableArea) + .InitiallyCollapsed(false) + .HeaderContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(AssetName) + .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") + ] + ] + .BodyContent() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Top) + .Padding(PaddingAmount) + [ + SNew(SBox) + .WidthOverride(96.0f) + .HeightOverride(96.0f) + [ + AssetThumbnail->MakeThumbnailWidget() + ] + ] + + SHorizontalBox::Slot() + .Padding(PaddingAmount) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Left) + .Padding(PaddingAmount) + [ + SNew(SHyperlink) + .Text(AssetPath) + .OnNavigate_Lambda([&]() { + const FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + TArray AssetDatas; + AssetDatas.Push(AssetData.Get()); + ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); + }) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(STextBlock) + .Visibility((RuleViolations.Get().Num() > 0) ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed) + .Text(FText::FormatNamed( + LOCTEXT("ErrorWarningDisplay", "{NumErrors} {NumErrors}|plural(one=Error,other=Errors), {NumWarnings} {NumWarnings}|plural(one=Warning,other=Warnings)"), + TEXT("NumErrors"), NumErrors, + TEXT("NumWarnings"), NumWarnings + )) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SLintReportAssetErrorList) + .RuleViolations(RuleViolations) + ] + ] + ] + ] + ] + ] + ]; + // clang-format on + // @formatter:on +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReportAssetError.cpp b/Source/Linter/Private/UI/LintReportAssetError.cpp new file mode 100644 index 0000000..a1436f5 --- /dev/null +++ b/Source/Linter/Private/UI/LintReportAssetError.cpp @@ -0,0 +1,112 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "UI/LintReportAssetError.h" + +#include "LinterStyle.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Layout/SExpandableArea.h" +#include "LintRule.h" +#include "Widgets/Layout/SBox.h" + +#define LOCTEXT_NAMESPACE "LintReport" + +void SLintReportAssetError::Construct(const FArguments& Args) { + RuleViolation = Args._RuleViolation; + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + const ULintRule* LintRule = RuleViolation.Get()->ViolatedRule.Get()->GetDefaultObject(); + check(LintRule != nullptr); + + const FSlateBrush* RuleIcon = nullptr; + const bool bHasURL = !LintRule->RuleURL.IsEmpty(); + + switch (LintRule->RuleSeverity) { + case ELintRuleSeverity::Error: RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Error"); + break; + case ELintRuleSeverity::Warning: RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Warning"); + break; + case ELintRuleSeverity::Info: RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Info"); + break; + //case ELintRuleSeverity::Ignore: + default: break; + } + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Center) + .Padding(PaddingAmount) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(PaddingAmount) + .AutoWidth() + .VAlign(VAlign_Center) + .HAlign(HAlign_Left) + [ + SNew(SBox) + .WidthOverride(14.0f) + .HeightOverride(14.0f) + [ + SNew(SImage) + .Image(RuleIcon) + ] + ] + + SHorizontalBox::Slot() + .Padding(PaddingAmount) + .AutoWidth() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + [ + SNew(STextBlock) + .Text(LintRule->RuleTitle) + .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") + ] + + SHorizontalBox::Slot() + .AutoWidth() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + [ + SNew(SBox) + .WidthOverride(16.0f) + .HeightOverride(16.0f) + [ + SNew(SImage) + .Cursor(EMouseCursor::Hand) + .Visibility(bHasURL ? EVisibility::Visible : EVisibility::Collapsed) + .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { + FPlatformProcess::LaunchURL(*RuleViolation.Get()->ViolatedRule.Get()->GetDefaultObject()->RuleURL, nullptr, nullptr); + return FReply::Handled(); + }) + .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Top) + .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) + [ + SNew(STextBlock) + .AutoWrapText(true) + .Text(LintRule->RuleDescription) + ] + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Top) + .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) + [ + SNew(STextBlock) + .AutoWrapText(true) + .Text(RuleViolation.Get()->RecommendedAction) + ] + ]; + // clang-format on + // @formatter:on +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReportAssetErrorList.cpp b/Source/Linter/Private/UI/LintReportAssetErrorList.cpp new file mode 100644 index 0000000..465e1fa --- /dev/null +++ b/Source/Linter/Private/UI/LintReportAssetErrorList.cpp @@ -0,0 +1,30 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "UI/LintReportAssetErrorList.h" +#include "LinterStyle.h" +#include "Framework/Views/ITypedTableView.h" +#include "UI/LintReportAssetError.h" +#include "LintRule.h" + +#define LOCTEXT_NAMESPACE "LintReport" + +void SLintReportAssetErrorList::Construct(const FArguments& Args) { + RuleViolations = Args._RuleViolations; + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + ChildSlot + [ + SNew(SListView>) + .SelectionMode(ESelectionMode::None) + .ListItemsSource(&RuleViolations.Get()) + .OnGenerateRow_Lambda([&](const TSharedPtr InItem, const TSharedRef& OwnerTable) { + return SNew(STableRow>, OwnerTable) + [ + SNew(SLintReportAssetError) + .RuleViolation(InItem) + ]; + }) + ]; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReportRuleDetails.cpp b/Source/Linter/Private/UI/LintReportRuleDetails.cpp new file mode 100644 index 0000000..041122a --- /dev/null +++ b/Source/Linter/Private/UI/LintReportRuleDetails.cpp @@ -0,0 +1,206 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#include "UI/LintReportRuleDetails.h" + +#include "AssetThumbnail.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "LinterStyle.h" +#include "LintRule.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Internationalization/Internationalization.h" +#include "Misc/EngineVersionComparison.h" +#include "UI/LintReportRuleErrorList.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Input/SHyperlink.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Layout/SExpandableArea.h" +#include "Widgets/Text/STextBlock.h" + + +#define LOCTEXT_NAMESPACE "LintReport" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +void SLintReportRuleDetails::Construct(const FArguments& Args) { + RuleViolations = Args._RuleViolations; + ThumbnailPool = Args._ThumbnailPool; + + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + check(RuleViolations.Get().Num() > 0); + ULintRule* BrokenRule = (RuleViolations.Get())[0]->ViolatedRule.GetDefaultObject(); + check(BrokenRule != nullptr); + + const FText RuleName = BrokenRule->RuleTitle; + const FText RuleDesc = BrokenRule->RuleDescription; + + RuleURL = BrokenRule->RuleURL; + + const FSlateBrush* RuleIcon = nullptr; + switch (BrokenRule->RuleSeverity) { + case ELintRuleSeverity::Error: RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Error"); + break; + case ELintRuleSeverity::Warning: RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Warning"); + break; + case ELintRuleSeverity::Info: RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Info"); + break; + //case ELintRuleSeverity::Ignore: + default: break; + } + + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + const IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + +#if UE_VERSION_NEWER_THAN(5, 1, 0) + RuleAssetData = AssetRegistry.GetAssetByObjectPath(BrokenRule->GetPathName(), true); +#else + RuleAssetData = AssetRegistry.GetAssetByObjectPath(FName(*BrokenRule->GetPathName()), true); +#endif + FText RuleAssetPath; + if (RuleAssetData.IsValid()) { + RuleAssetPath = FText::FromName(RuleAssetData.PackagePath); + } + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(PaddingAmount) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(PaddingAmount) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SExpandableArea) + .InitiallyCollapsed(false) + .HeaderContent() + [ + SNew(SHorizontalBox) + // Rule Thumbnail + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Top) + .Padding(PaddingAmount) + [ + SAssignNew(ThumbnailBox, SBox) + .WidthOverride(96.0f) + .HeightOverride(96.0f) + .Visibility(RuleAssetData.IsValid() ? EVisibility::HitTestInvisible : EVisibility::Collapsed) + ] + // Rule Icon + + SHorizontalBox::Slot() + .Padding(PaddingAmount) + .AutoWidth() + .VAlign(VAlign_Center) + .HAlign(HAlign_Left) + [ + SNew(SBox) + .WidthOverride(14.0f) + .HeightOverride(14.0f) + [ + SNew(SImage) + .Image(RuleIcon) + ] + ] + // Rule Name + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(STextBlock) + .Text(RuleName) + .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") + ] + // Link to Rule URL + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(8.0f, 0.0) + [ + SNew(SImage) + .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) + .Cursor(EMouseCursor::Hand) + .Visibility(RuleURL.IsEmpty() ? EVisibility::Collapsed : EVisibility::Visible) + .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { + FPlatformProcess::LaunchURL(*RuleURL, nullptr, nullptr); + return FReply::Handled(); + }) + ] + // Link to Rule Definition Asset + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(8.0f, 0.0) + [ + SNew(SImage) + .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) + .Cursor(EMouseCursor::Hand) + .Visibility(EVisibility::Collapsed) + .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { + const FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + ContentBrowserModule.Get().SyncBrowserToAssets(TArray({RuleAssetData})); + return FReply::Handled(); + }) + ] + // Asset Count + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .AutoWidth() + .Padding(8.0f, 0.0) + [ + SNew(SHyperlink) + .Text(FText::FormatNamed(LOCTEXT("AssetCountDisplay", "{NumAssets} {NumAssets}|plural(one=Asset,other=Assets)"), TEXT("NumAssets"), RuleViolations.Get().Num())) + .OnNavigate_Lambda([&]() { + const FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + TArray AssetDatas; + + for (const TSharedPtr& RuleViolation : RuleViolations.Get()) { + AssetDatas.Push(RuleViolation->ViolatorAssetData); + } + + ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); + }) + ] + ] + .BodyContent() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .Padding(PaddingAmount) + .HAlign(HAlign_Left) + .AutoHeight() + [ + SNew(STextBlock) + .Text(RuleDesc) + .AutoWrapText(true) + .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") + ] + + SVerticalBox::Slot() + .Padding(PaddingAmount) + .HAlign(HAlign_Left) + .AutoHeight() + [ + SNew(SLintReportRuleErrorList) + .RuleViolations(RuleViolations) + ] + ] + ] + ] + ] + ]; + // clang-format on + // @formatter:on + + if (RuleAssetData.IsValid()) { + const TSharedPtr RuleThumbnail = MakeShareable(new FAssetThumbnail(RuleAssetData, 96, 96, ThumbnailPool.Get())); + ThumbnailBox->SetContent(RuleThumbnail->MakeThumbnailWidget()); + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReportRuleError.cpp b/Source/Linter/Private/UI/LintReportRuleError.cpp new file mode 100644 index 0000000..89cd9a1 --- /dev/null +++ b/Source/Linter/Private/UI/LintReportRuleError.cpp @@ -0,0 +1,68 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "UI/LintReportRuleError.h" + +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "LinterStyle.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Layout/SExpandableArea.h" +#include "Widgets/Input/SHyperlink.h" +#include "LintRule.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Misc/EngineVersionComparison.h" + +#define LOCTEXT_NAMESPACE "LintReport" + +void SLintReportRuleError::Construct(const FArguments& Args) { + RuleViolation = Args._RuleViolation; + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Center) + .Padding(PaddingAmount) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(PaddingAmount) + .AutoWidth() + .HAlign(HAlign_Left) + .VAlign(VAlign_Center) + [ + SNew(SHyperlink) + .Text(FText::FromName(RuleViolation.Get()->ViolatorAssetData.PackageName)) + .OnNavigate_Lambda([&]() { + const FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + TArray AssetDatas; + #if UE_VERSION_NEWER_THAN(5, 1, 0) + AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(RuleViolation.Get()->ViolatorAssetData.GetSoftObjectPath())); + #else + AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(RuleViolation.Get()->ViolatorAssetData.ObjectPath)); + #endif + ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); + }) + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Top) + .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) + [ + SNew(STextBlock) + .AutoWrapText(true) + .Text(RuleViolation.Get()->RecommendedAction) + .Visibility(RuleViolation.Get()->RecommendedAction.IsEmpty() ? EVisibility::Collapsed : EVisibility::SelfHitTestInvisible) + ] + ]; + // clang-format on + // @formatter:on +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintReportRuleErrorList.cpp b/Source/Linter/Private/UI/LintReportRuleErrorList.cpp new file mode 100644 index 0000000..8f62866 --- /dev/null +++ b/Source/Linter/Private/UI/LintReportRuleErrorList.cpp @@ -0,0 +1,35 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#include "UI/LintReportRuleErrorList.h" +#include "LinterStyle.h" +#include "Framework/Views/ITypedTableView.h" +#include "LintRule.h" +#include "UI/LintReportRuleError.h" + +#define LOCTEXT_NAMESPACE "LintReport" + +void SLintReportRuleErrorList::Construct(const FArguments& Args) { + RuleViolations = Args._RuleViolations; + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SListView>) + .SelectionMode(ESelectionMode::None) + .ListItemsSource(&RuleViolations.Get()) + .OnGenerateRow_Lambda([&](const TSharedPtr InItem, const TSharedRef& OwnerTable) { + return + SNew(STableRow>, OwnerTable) + [ + SNew(SLintReportRuleError) + .RuleViolation(InItem) + ]; + }) + ]; + // clang-format on + // @formatter:on +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/LintWizard.cpp b/Source/Linter/Private/UI/LintWizard.cpp new file mode 100644 index 0000000..7a61d46 --- /dev/null +++ b/Source/Linter/Private/UI/LintWizard.cpp @@ -0,0 +1,698 @@ +// Copyright 2015-2017 by Gamemakin LLC +#include "UI/LintWizard.h" + +#include "CoreGlobals.h" +#include "Delegates/Delegate.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "AssetToolsModule.h" +#include "ContentBrowserModule.h" +#include "DesktopPlatformModule.h" +#include "AssetRegistry/IAssetRegistry.h" +#include "SlateOptMacros.h" +#include "Widgets/Layout/SSeparator.h" +#include "Widgets/Text/SRichTextBlock.h" +#include "Widgets/Notifications/SNotificationList.h" +#include "Widgets/Layout/SScrollBox.h" +#include "IUATHelperModule.h" +#include "Misc/FeedbackContext.h" +#include "Framework/Notifications/NotificationManager.h" +#include "FileHelpers.h" +#include "Logging/MessageLog.h" +#include "Logging/TokenizedMessage.h" +#include "Framework/Docking/TabManager.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Docking/SDockTab.h" +#include "Misc/App.h" +#include "Engine/World.h" +#include "Misc/EngineVersionComparison.h" +#include "LinterStyle.h" +#include "LintRuleSet.h" +#include "LinterSettings.h" +#include "UI/SAssetLinkWidget.h" + + +#define LOCTEXT_NAMESPACE "LintWizard" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SLintWizard::Construct(const FArguments& InArgs) { + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + + RuleSets = TArray>(); + + // Try to load the default rule set + const ULintRuleSet* DefaultRuleSet = GetDefault()->DefaultLintRuleSet.LoadSynchronous(); + + // Even though this happens on module startup, we try force loading all rule sets again in case any new unloaded rule sets have been added + // or for some reason our existing rule sets were unloaded from memory + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); + const IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + TArray FoundRuleSets; +#if UE_VERSION_NEWER_THAN(5, 1, 0) + AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetClassPathName(), FoundRuleSets, true); +#else + AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); +#endif + + + // Attempt to get all RuleSets in memory so that linting tools are better aware of them + for (const FAssetData& RuleSetData : FoundRuleSets) { + const ULintRuleSet* LoadedRuleSet = Cast(RuleSetData.GetAsset()); + if (LoadedRuleSet != nullptr) { + FAssetData* newData = new FAssetData; + *newData = RuleSetData; + RuleSets.Push(MakeShareable(newData)); + if (LoadedRuleSet == DefaultRuleSet) { + SelectedRuleSet = RuleSets.Last(); + } + } + } + + if (!SelectedRuleSet.IsValid() && RuleSets.Num() > 0) { + SelectedRuleSet = RuleSets[0]; + } + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SBorder) + .Padding(18) + .BorderImage(FAppStyle::GetBrush("Docking.Tab.ContentAreaBrush")) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + [ + SAssignNew(MainWizard, SWizard) + .ShowPageList(false) + .ShowCancelButton(false) + .ButtonStyle(FAppStyle::Get(), "FlatButton.Default") + .CancelButtonStyle(FAppStyle::Get(), "FlatButton.Default") + .FinishButtonStyle(FAppStyle::Get(), "FlatButton.Success") + .ButtonTextStyle(FAppStyle::Get(), "LargeText") +#if UE_VERSION_OLDER_THAN(5, 0, 0) + .ForegroundColor(FEditorStyle::Get().GetSlateColor("WhiteBrush")) +#endif + .CanFinish(true) + .FinishButtonText(LOCTEXT("FinishButtonText", "Close")) + .OnFinished_Lambda([&]() { +#if UE_VERSION_NEWER_THAN(4, 26, 0) + FGlobalTabmanager::Get()->TryInvokeTab(FName("LinterTab"))->RequestCloseTab(); +#else + FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab"))->RequestCloseTab(); +#endif + }) + + SWizard::Page() + .CanShow_Lambda([&]() { + return RuleSets.Num() > 0; + }) + [ + SNew(SVerticalBox) + // Title + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0) + [ + SNew(STextBlock) + .Font(FAppStyle::Get().GetFontStyle("HeadingSmall")) + .Text(LOCTEXT("LinterSelectionTitle", "Linter Rule Set Selection")) + ] + // Title spacer + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0, 2, 0, 8) + [ + SNew(SSeparator) + ] + // Linter Selection + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SAssignNew(RuleSetSelectionComboBox, SComboBox>) + .OptionsSource(&RuleSets) + .InitiallySelectedItem(SelectedRuleSet) + .OnGenerateWidget_Lambda([&](TSharedPtr LintRuleSet) { + ULintRuleSet* RuleSet = Cast(LintRuleSet->GetAsset()); + if (RuleSet != nullptr) { + return + SNew(STextBlock) + .Text(RuleSet->RuleSetDescription.IsEmpty() ? FText::FromString(RuleSet->GetPathName()) : RuleSet->RuleSetDescription); + } + + return + SNew(STextBlock) + .Text(FText::FromString(TEXT("This Lint Rule Set Failed To Load? Uhhhh...."))); + }) + .OnSelectionChanged_Lambda([&](TSharedPtr Item, ESelectInfo::Type SelectInfo) { + SelectedRuleSet = Item; + RuleSetSelectionComboBox->RefreshOptions(); + }) + .ContentPadding(4.0f) + [ + SNew(STextBlock) + .Text_Lambda([&]() { + return SelectedRuleSet->GetAsset() != nullptr ? Cast(SelectedRuleSet->GetAsset())->RuleSetDescription : FText::GetEmpty(); + }) + ] + ] + ] + // Lint Report + + SWizard::Page() + .OnEnter(this, &SLintWizard::OnLintReportEntered) + .CanShow_Lambda([&]() { + return RuleSets.Num() >= 1 && SelectedRuleSet.IsValid(); + }) + [ + SNew(SVerticalBox) + // Title + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0) + [ + SNew(STextBlock) + .Font(FAppStyle::Get().GetFontStyle("HeadingSmall")) + .Text(LOCTEXT("LinterReportTitle", "Lint Report")) + ] + // Marketplace No Errors Required Text + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(STextBlock) + .Text(LOCTEXT("MarketplaceNoErrorsRequired", "The Epic Marketplace requires you to have zero linting errors before submission and approval.")) + .Visibility_Lambda([&]() { + return (LintReport.IsValid() && LintReport->bHasRanReport && LintReport->LintResults->Errors > 0 && LintReport->LastUsedRuleSet != nullptr && LintReport->LastUsedRuleSet->bShowMarketplacePublishingInfoInLintWizard) ? EVisibility::HitTestInvisible : EVisibility::Collapsed; + }) + ] + // Title spacer + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0, 2, 0, 8) + [ + SNew(SSeparator) + ] + // Linter Report + + SVerticalBox::Slot() + .FillHeight(1.0f) + .VAlign(VAlign_Fill) + .HAlign(HAlign_Fill) + .Padding(PaddingAmount) + [ + SAssignNew(LintReport, SLintReport) + ] + ] + // Marketplace Info Page + + SWizard::Page() + .OnEnter(this, &SLintWizard::OnMarketplaceRecommendationsEntered) + .CanShow_Lambda([&]() { + return LintReport.IsValid() && LintReport->bHasRanReport && LintReport->LintResults->Errors <= 0 && LintReport->LastUsedRuleSet != nullptr && LintReport->LastUsedRuleSet->bShowMarketplacePublishingInfoInLintWizard; + }) + [ + SNew(SVerticalBox) + // Title + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0) + [ + SNew(STextBlock) + .Font(FAppStyle::Get().GetFontStyle("HeadingSmall")) + .Text(LOCTEXT("MarketplaceInfoTitle", "Marketplace Recommendations")) + ] + // Title spacer + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0, 2, 0, 8) + [ + SNew(SSeparator) + ] + + SVerticalBox::Slot() + .FillHeight(1.0f) + [ + SNew(SScrollBox) + + SScrollBox::Slot() + [ + SNew(SVerticalBox) + // Disclaimer + + SVerticalBox::Slot() + .VAlign(VAlign_Top) + .AutoHeight() + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(PaddingAmount) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(PaddingAmount) + [ + SNew(SHorizontalBox) + // Info image + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Center) + .Padding(28.0f, 8.0f) + [ + SNew(SImage) + .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Info")) + ] + // Disclaimer text + + SHorizontalBox::Slot() + .VAlign(VAlign_Center) + .FillWidth(1.0f) + [ + SNew(SRichTextBlock) + .Text(LOCTEXT("SuccessDisclaimer", "This product has successfully passed the Marketplace Guidlines Linter scan. Please note, however, that this does not guarantee this product's acceptance onto the Marketplace. We also recommend that you take the following actions listed below.")) + .AutoWrapText(true) + .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") + ] + ] + ] + ] + // Fix Up Redirectors step widget + + SVerticalBox::Slot() + .AutoHeight() + .VAlign(VAlign_Fill) + .HAlign(HAlign_Fill) + .Padding(PaddingAmount) + [ + SNew(SStepWidget) + .StepName(LOCTEXT("FixUpRedirectsStepName", "Fix Up Redirectors")) + .StepDesc(LOCTEXT("FixUpRedirectsStepDesc", "Resave all packages that point to redirectors in your project, and delete those redirectors if able to resave all the things referencing them.")) + .Icon(FLinterStyle::Get()->GetBrush("Linter.Step.FixUpRedirects.Thumbnail")) + .ShowStepStatusIcon(false) + .StepStatus_Lambda([this]() { + return FixUpRedirectorStatus; + }) + .StepActionText(LOCTEXT("FixUpRedirectsStepAction", "Fix Up Redirectors")) + .OnPerformAction_Lambda([this](FScopedSlowTask& ScopedSlowTask) { + FixUpRedirectorStatus = InProgress; + bool bSuccess = true; + + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::Get().LoadModuleChecked(TEXT("AssetRegistry")); + ScopedSlowTask.EnterProgressFrame(0, LOCTEXT("Linter.FixUpRedirects.FindingRedirectors", "Looking For redirectors...")); + + // Form a filter from the paths + FARFilter Filter; + Filter.bRecursivePaths = true; + Filter.PackagePaths.Add("/Game"); + #if UE_VERSION_NEWER_THAN(5, 1, 0) + Filter.ClassPaths.Add(FTopLevelAssetPath{"ObjectRedirector"}); + #else + Filter.ClassNames.Add("ObjectRedirector"); + #endif + + // Query for a list of assets in the selected paths + TArray AssetList; + AssetRegistryModule.Get().GetAssets(Filter, AssetList); + + if (AssetList.Num() > 0) { + TArray ObjectPaths; + for (const auto& Asset : AssetList) { + #if UE_VERSION_NEWER_THAN(5, 1, 0) + ObjectPaths.Add(Asset.GetObjectPathString()); + #else + ObjectPaths.Add(Asset.ObjectPath.ToString()); + #endif + } + + ScopedSlowTask.EnterProgressFrame(0.25f, LOCTEXT("Linter.FixUpRedirects.LoadingRedirectors", "Loading redirectors...")); + + TArray Objects; + if (LoadAssetsIfNeeded(ObjectPaths, Objects)) { + // Transform Objects array to ObjectRedirectors array + TArray Redirectors; + for (auto&& Object : Objects) { + auto Redirector = CastChecked(Object); + Redirectors.Add(Redirector); + } + + ScopedSlowTask.EnterProgressFrame(0.25f, LOCTEXT("Linter.FixUpRedirects.FixingRedirectors", "Fixing up redirectors...")); + + // Load the asset tools module + FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked(TEXT("AssetTools")); + AssetToolsModule.Get().FixupReferencers(Redirectors); + } else { + FNotificationInfo NotificationInfo(LOCTEXT("FixUpRedirectorsFailed", "Linter failed to load an object redirector when trying to fix up all redirectors.")); + NotificationInfo.ExpireDuration = 6.0f; + NotificationInfo.Hyperlink = FSimpleDelegate::CreateStatic([]() { + FMessageLog("LoadErrors").Open(EMessageSeverity::Info, true); + }); + NotificationInfo.HyperlinkText = LOCTEXT("LoadObjectHyperlink", "Show Message Log"); + FSlateNotificationManager::Get().AddNotification(NotificationInfo); + bSuccess = false; + } + } + + FixUpRedirectorStatus = bSuccess ? Success : Error; + }) + ] + // Build Lighting Widget + + SVerticalBox::Slot() + .Padding(PaddingAmount) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(PaddingAmount) + .Visibility_Lambda([&]() { + return (MapAssetDataList.Num() > 0) ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed; + }) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(PaddingAmount) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SHorizontalBox) + // Template thumbnail image + + SHorizontalBox::Slot() + .Padding(4.0) + .AutoWidth() + .VAlign(VAlign_Top) + [ + SNew(SImage) + .Image(FLinterStyle::Get()->GetBrush("Linter.Step.BuildLighting.Thumbnail")) + ] + // Template name and description + + SHorizontalBox::Slot() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(STextBlock) + .Text(LOCTEXT("BuildLightingStepName", "Build Lighting and Run Map Check")) + .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") + ] + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SRichTextBlock) + .Text(LOCTEXT("BuildLightingStepDesc", "Open each map listed below and click Map Check in the Build Options Menu of the Level Editor Toolbar. If any Map Check errors are generated, resolve them before packaging your project. If any 'LIGHTING NEEDS TO BE REBUILT' errors are present press Ctrl+Shift+Semicolon to rebuild lighting.")) + .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") + .AutoWrapText(true) + ] + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SSeparator) + ] + + SVerticalBox::Slot() + .VAlign(VAlign_Fill) + .FillHeight(1.0f) + .Padding(PaddingAmount) + [ + SAssignNew(MarketplaceRecommendationMapScrollBoxPtr, SScrollBox) + .ScrollBarAlwaysVisible(true) + ] + ] + ] + ] + // Save All Step + + SVerticalBox::Slot() + .Padding(PaddingAmount) + .AutoHeight() + [ + SNew(SStepWidget) + .StepName(LOCTEXT("SaveAllStepName", "Save All")) + .StepDesc(LOCTEXT("SaveAllStepDesc", "Save all unsaved levels and assets to disk.")) + .Icon(FLinterStyle::Get()->GetBrush("Linter.Step.SaveAll.Thumbnail")) + .ShowStepStatusIcon(false) + .StepStatus_Lambda([this]() { + return SaveAllStatus; + }) + .StepActionText(LOCTEXT("SaveAllStepNameAction", "Save All")) + .OnPerformAction_Lambda([this](FScopedSlowTask& ScopedSlowTask) { + SaveAllStatus = InProgress; + + // Taken from MainFrameActions.cpp SaveAll + constexpr bool bPromptUserToSave = false; + constexpr bool bSaveMapPackages = true; + constexpr bool bSaveContentPackages = true; + constexpr bool bFastSave = false; + constexpr bool bNotifyNoPackagesSaved = false; + constexpr bool bCanBeDeclined = false; + if (FEditorFileUtils::SaveDirtyPackages(bPromptUserToSave, bSaveMapPackages, bSaveContentPackages, bFastSave, bNotifyNoPackagesSaved, bCanBeDeclined)) { + SaveAllStatus = Success; + } else { + FNotificationInfo NotificationInfo(LOCTEXT("SaveAllFailed", "Linter failed to save all dirty assets!")); + NotificationInfo.ExpireDuration = 6.0f; + NotificationInfo.Hyperlink = FSimpleDelegate::CreateStatic([]() { + FMessageLog("LoadErrors").Open(EMessageSeverity::Info, true); + }); + NotificationInfo.HyperlinkText = LOCTEXT("LoadObjectHyperlink", "Show Message Log"); + FSlateNotificationManager::Get().AddNotification(NotificationInfo); + SaveAllStatus = Error; + } + }) + ] + // Package Project step + + SVerticalBox::Slot() + .Padding(PaddingAmount) + .AutoHeight() + [ + SNew(SBorder) + .Visibility(EVisibility::Collapsed) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(PaddingAmount) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(PaddingAmount) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(4.0) + .AutoWidth() + .VAlign(VAlign_Top) + [ + SNew(SImage) + .Image(FLinterStyle::Get()->GetBrush("Linter.Step.Package.Thumbnail")) + ] + + SHorizontalBox::Slot() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(STextBlock) + .Text(LOCTEXT("PackageProduct", "Package Product to .Zip")) + .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") + ] + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SRichTextBlock) + .Text(LOCTEXT("PackageProductDesc", "Upload this .zip file to a hosting site (GoogleDrive/Dropbox/etc.) and enter the download URL as the associated product's Project File Link in the Publisher Portal.")) + .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") + .AutoWrapText(true) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .OnClicked_Lambda([&]() { + #if PLATFORM_WINDOWS + FText PlatformName = LOCTEXT("PlatformName_Windows", "Windows"); + #elif PLATFORM_MAC + FText PlatformName = LOCTEXT("PlatformName_Mac", "Mac"); + #elif PLATFORM_LINUX + FText PlatformName = LOCTEXT("PlatformName_Linux", "Linux"); + #else + FText PlatformName = LOCTEXT("PlatformName_Other", "Other OS"); + #endif + + bool bOpened = false; + TArray SaveFilenames; + IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); + if (DesktopPlatform != nullptr) { + bOpened = DesktopPlatform->SaveFileDialog( + nullptr, + NSLOCTEXT("UnrealEd", "ZipUpProject", "Zip file location").ToString(), + FPaths::ProjectDir(), + FApp::GetProjectName(), + TEXT("Zip file|*.zip"), + EFileDialogFlags::None, + SaveFilenames); + } + + // We never want to compile editor targets when invoking UAT in this context. + // If we are installed or don't have a compiler, we must assume we have a precompiled UAT. + const TCHAR* UATFlags = (FApp::GetEngineIsPromotedBuild() || FApp::IsEngineInstalled()) + ? TEXT("-nocompile -nocompileeditor") + : TEXT("-nocompileeditor"); + + if (bOpened) { + for (FString FileName : SaveFilenames) { + // Ensure path is full rather than relative (for macs) + FString FinalFileName = FPaths::ConvertRelativePathToFull(FileName); + FString ProjectPath = FPaths::IsProjectFilePathSet() ? FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()) : FPaths::RootDir() / FApp::GetProjectName(); + + FString CommandLine = FString::Printf(TEXT("ZipProjectUp %s -project=\"%s\" -install=\"%s\""), UATFlags, *ProjectPath, *FinalFileName); + + IUATHelperModule::Get().CreateUatTask( + CommandLine, + PlatformName, + LOCTEXT("ZipTaskName", "Zipping Up Project"), + LOCTEXT("ZipTaskShortName", "Zip Project Task"), + FAppStyle::GetBrush(TEXT("MainFrame.CookContent")) + ); + } + + #if UE_VERSION_NEWER_THAN(4, 26, 0) + FGlobalTabmanager::Get()->TryInvokeTab(FName("LinterTab"))->RequestCloseTab(); + #else + FGlobalTabmanager::Get()->InvokeTab(FName("LinterTab"))->RequestCloseTab(); + #endif + } + + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Text(LOCTEXT("SelectOutputFolder", "Package Project into a .zip")) + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ]; + // clang-format on + // @formatter:on + + // Determine all levels in the project for wizard purposes + FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + TArray AssetDatas; + FARFilter Filter; +#if UE_VERSION_NEWER_THAN(5, 1, 0) + Filter.ClassPaths.Add(FTopLevelAssetPath(UWorld::StaticClass())); +#else + Filter.ClassNames.Add(UWorld::StaticClass()->GetFName()); +#endif + Filter.bRecursivePaths = true; + Filter.PackagePaths.Add(TEXT("/Game")); + AssetRegistryModule.Get().GetAssets(Filter, AssetDatas); + + MapAssetDataList.Empty(); + for (FAssetData Asset : AssetDatas) { + MapAssetDataList.Add(MakeShareable(new FAssetData(Asset))); + + // clang-format off + // @formatter:off + MarketplaceRecommendationMapScrollBoxPtr.Get()->AddSlot() + .HAlign(HAlign_Fill) + .VAlign(VAlign_Fill) + .Padding(PaddingAmount) + [ + SNew(SAssetLinkWidget) + .AssetData(Asset) + ]; + // clang-format on + // @formatter:on + } +} + +END_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SLintWizard::OnLintReportEntered() { + LintReport->Rebuild(CastChecked(SelectedRuleSet->GetAsset())); +} + +void SLintWizard::OnMarketplaceRecommendationsEntered() { + bOfferPackage = true; +} + +bool SLintWizard::LoadAssetsIfNeeded(const TArray& ObjectPaths, TArray& LoadedObjects) { + bool bAnyObjectsWereLoadedOrUpdated = false; + + // Build a list of unloaded assets + TArray UnloadedObjectPaths; + bool bAtLeastOneUnloadedMap = false; + for (int32 PathIdx = 0; PathIdx < ObjectPaths.Num(); ++PathIdx) { + const FString& ObjectPath = ObjectPaths[PathIdx]; + + UObject* FoundObject = FindObject(nullptr, *ObjectPath); + if (FoundObject) { + LoadedObjects.Add(FoundObject); + } else { + // Unloaded asset, we will load it later + UnloadedObjectPaths.Add(ObjectPath); + if (FEditorFileUtils::IsMapPackageAsset(ObjectPath)) { + bAtLeastOneUnloadedMap = true; + } + } + } + + // Make sure all selected objects are loaded, where possible + if (UnloadedObjectPaths.Num() > 0) { + FScopedSlowTask SlowTask(UnloadedObjectPaths.Num(), LOCTEXT("LoadingObjects", "Loading Objects...")); + SlowTask.MakeDialog(); + + GIsEditorLoadingPackage = true; + + constexpr ELoadFlags LoadFlags = LOAD_None; + bool bSomeObjectsFailedToLoad = false; + for (int32 PathIdx = 0; PathIdx < UnloadedObjectPaths.Num(); ++PathIdx) { + const FString& ObjectPath = UnloadedObjectPaths[PathIdx]; + SlowTask.EnterProgressFrame(1, FText::Format(LOCTEXT("LoadingObjectf", "Loading {0}..."), FText::FromString(ObjectPath))); + + // Load up the object + UObject* LoadedObject = LoadObject(nullptr, *ObjectPath, nullptr, LoadFlags, nullptr); + if (LoadedObject) { + LoadedObjects.Add(LoadedObject); + } else { + bSomeObjectsFailedToLoad = true; + } + + if (GWarn->ReceivedUserCancel()) { + // If the user has canceled stop loading the remaining objects. We don't add the remaining objects to the failed string, + // this would only result in launching another dialog when by their actions the user clearly knows not all of the + // assets will have been loaded. + break; + } + } + GIsEditorLoadingPackage = false; + + if (bSomeObjectsFailedToLoad) { + return false; + } + } + + return true; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Private/UI/SAssetLinkWidget.cpp b/Source/Linter/Private/UI/SAssetLinkWidget.cpp new file mode 100644 index 0000000..3e099a2 --- /dev/null +++ b/Source/Linter/Private/UI/SAssetLinkWidget.cpp @@ -0,0 +1,48 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "UI/SAssetLinkWidget.h" +#include "ContentBrowserModule.h" +#include "IContentBrowserSingleton.h" +#include "LinterStyle.h" +#include "SlateOptMacros.h" +#include "AssetRegistry/AssetRegistryModule.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Input/SHyperlink.h" +#include "Misc/EngineVersionComparison.h" + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SAssetLinkWidget::Construct(const FArguments& Args) { + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + AssetData = Args._AssetData; + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SHyperlink) + .Text(FText::FromName(AssetData.Get().AssetName)) + .Padding(PaddingAmount) + .OnNavigate_Lambda([&]() { + const FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); + const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + + TArray AssetDatas; +#if UE_VERSION_NEWER_THAN(5, 1, 0) + AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(AssetData.Get().GetObjectPathString())); +#else + AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(AssetData.Get().ObjectPath)); +#endif + ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); + }) + ] + ]; + // clang-format on + // @formatter:on +} + +END_SLATE_FUNCTION_BUILD_OPTIMIZATION diff --git a/Source/Linter/Private/UI/SStepWidget.cpp b/Source/Linter/Private/UI/SStepWidget.cpp new file mode 100644 index 0000000..fcfe505 --- /dev/null +++ b/Source/Linter/Private/UI/SStepWidget.cpp @@ -0,0 +1,161 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#include "UI/SStepWidget.h" + +#include "LinterStyle.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Text/STextBlock.h" +#include "Widgets/Images/SThrobber.h" +#include "Widgets/Text/SRichTextBlock.h" +#include "Misc/EngineVersionComparison.h" + +#if UE_VERSION_OLDER_THAN(5, 1, 0) +using FAppStyle = FEditorStyle; +#endif + +bool SStepWidget::IsStepCompleted(const bool bAllowWarning) const { + const EStepStatus Status = StepStatus.Get(); + if (bAllowWarning && Status == Warning) { + return true; + } + + return Status == Success; +} + +void SStepWidget::Construct(const FArguments& Args) { + const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); + + StepStatus = Args._StepStatus; + OnPerformAction = Args._OnPerformAction; + StepActionText = Args._StepActionText; + ShowStepStatusIcon = Args._ShowStepStatusIcon; + + // Visibility lambda based on whether step is in progress + auto VisibleIfInProgress = [this]() { + return StepStatus.Get(NoStatus) == InProgress ? EVisibility::Visible : EVisibility::Collapsed; + }; + + // Enabled lambda based on whether this widget has a step status that requires action + auto EnabledBasedOnStepStatus = [this]() -> bool { + switch (StepStatus.Get(NoStatus)) { + case NoStatus: + case InProgress: + case Success: return false; + case Unknown: + case Warning: + case Error: + case NeedsUpdate: return true; + } + return false; + }; + + // clang-format off + // @formatter:off + ChildSlot + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("NoBorder")) + .Padding(PaddingAmount) + [ + SNew(SBorder) + .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) + .Padding(PaddingAmount) + [ + SNew(SHorizontalBox) + // Status Image + + SHorizontalBox::Slot() + .Padding(PaddingAmount) + .AutoWidth() + [ + SNew(SImage) + .Visibility_Lambda([&]() { + return StepStatus.Get(NoStatus) == NoStatus || !ShowStepStatusIcon.Get(true) ? EVisibility::Collapsed : EVisibility::Visible; + }) + .Image_Lambda([&]() { + switch (StepStatus.Get(NoStatus)) { + case NoStatus: + case Unknown: return FLinterStyle::Get()->GetBrush("Linter.Step.Unknown"); + case InProgress: + case NeedsUpdate: return FLinterStyle::Get()->GetBrush("Linter.Step.Working"); + case Warning: return FLinterStyle::Get()->GetBrush("Linter.Step.Warning"); + case Error: return FLinterStyle::Get()->GetBrush("Linter.Step.Error"); + case Success: return FLinterStyle::Get()->GetBrush("Linter.Step.Good"); + } + + return FLinterStyle::Get()->GetBrush("Linter.Step.Unknown"); + }) + ] + // Template thumbnail image + + SHorizontalBox::Slot() + .Padding(4.0) + .AutoWidth() + .VAlign(VAlign_Top) + [ + SNew(SImage) + .Visibility(Args._Icon.IsSet() ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed) + .Image(Args._Icon) + ] + // Template name and description + + SHorizontalBox::Slot() + [ + SNew(SVerticalBox) + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(STextBlock) + .Text(Args._StepName) + .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") + ] + + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SRichTextBlock) + .Text(Args._StepDesc) + .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") + .AutoWrapText(true) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(PaddingAmount) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SButton) + .IsEnabled_Lambda(EnabledBasedOnStepStatus) + .Visibility_Lambda([&]() { + return StepStatus.Get(NoStatus) == NoStatus ? EVisibility::Collapsed : EVisibility::Visible; + }) + .OnClicked_Lambda([&]() { + FScopedSlowTask SlowTask(1.0f, StepActionText.Get(FText())); + SlowTask.MakeDialog(); + + OnPerformAction.ExecuteIfBound(SlowTask); + return FReply::Handled(); + }) + [ + SNew(STextBlock) + .Text(StepActionText) + ] + ] + + SHorizontalBox::Slot() + .AutoWidth() + [ + SNew(SThrobber) + .Visibility_Lambda(VisibleIfInProgress) + ] + ] + ] + ] + ] + ]; + // clang-format off + // @formatter:off +} diff --git a/Plugins/Linter/Source/Linter/Public/AnyObject_LinterDummyClass.h b/Source/Linter/Public/AnyObject_LinterDummyClass.h similarity index 50% rename from Plugins/Linter/Source/Linter/Public/AnyObject_LinterDummyClass.h rename to Source/Linter/Public/AnyObject_LinterDummyClass.h index 5d697bf..4142634 100644 --- a/Plugins/Linter/Source/Linter/Public/AnyObject_LinterDummyClass.h +++ b/Source/Linter/Public/AnyObject_LinterDummyClass.h @@ -1,16 +1,14 @@ // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. #pragma once -#include "CoreMinimal.h" + #include "AnyObject_LinterDummyClass.generated.h" + UCLASS(BlueprintType, Blueprintable) -class UAnyObject_LinterDummyClass : public UObject -{ - GENERATED_BODY() +class UAnyObject_LinterDummyClass : public UObject { + GENERATED_BODY() public: - - UAnyObject_LinterDummyClass(const FObjectInitializer& ObjectInitializer); + UAnyObject_LinterDummyClass(const FObjectInitializer& ObjectInitializer); }; - diff --git a/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h b/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h new file mode 100644 index 0000000..80c5bf9 --- /dev/null +++ b/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h @@ -0,0 +1,101 @@ +// Copyright 2016 Gamemakin LLC. All Rights Reserved. + +#pragma once + +#include "Widgets/Input/SEditableTextBox.h" +#include "Widgets/Input/SCheckBox.h" +#include "Widgets/SWindow.h" +#include "Widgets/SCompoundWidget.h" + +#define LOCTEXT_NAMESPACE "LinterBatchRenamer" + +/** +* FDlgBatchRenameTool +* +* Wrapper class for SDlgBatchRenameTool. This class creates and launches a dialog then awaits the +* result to return to the user. +*/ +class FDlgBatchRenameTool { +public: + enum EResult { + Cancel = 0, + // No/Cancel, normal usage would stop the current action + Confirm = 1, + // Yes/Ok/Etc, normal usage would continue with action + }; + + FDlgBatchRenameTool(const TArray Assets); + + /** Shows the dialog box and waits for the user to respond. */ + EResult ShowModal(); + + FString Prefix; + FString Suffix; + bool bRemovePrefix; + bool bRemoveSuffix; + + FString Find; + FString Replace; + +private: + /** Cached pointer to the modal window */ + TSharedPtr DialogWindow; + + /** Cached pointer to the batch rename tool widget */ + TSharedPtr DialogWidget; + + const TArray SelectedAssets; +}; + +/** +* Slate panel for batch renaming +*/ +class SDlgBatchRenameTool : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SDlgBatchRenameTool) {} + /** Window in which this widget resides */ + SLATE_ATTRIBUTE(TSharedPtr, ParentWindow) + SLATE_END_ARGS() + + /** + * Constructs this widget + * + * @param InArgs The declaration data for this widget + */ + void Construct(const FArguments& InArgs); + + /** + * Returns the EResult of the button which the user pressed. Closing of the dialog + * in any other way than clicking "Ok" results in this returning a "Cancel" value + */ + FDlgBatchRenameTool::EResult GetUserResponse() const; + +private: + /** + * Handles when a button is pressed, should be bound with appropriate EResult Key + * + * @param ButtonID - The return type of the button which has been pressed. + */ + FReply OnButtonClick(FDlgBatchRenameTool::EResult ButtonID) { + ParentWindow->RequestDestroyWindow(); + UserResponse = ButtonID; + + return FReply::Handled(); + } + + /** Stores the users response to this dialog */ + FDlgBatchRenameTool::EResult UserResponse; + + /** Pointer to the window which holds this Widget, required for modal control */ + TSharedPtr ParentWindow; + +public: + TSharedPtr PrefixTextBox; + TSharedPtr SuffixTextBox; + TSharedPtr FindTextBox; + TSharedPtr ReplaceTextBox; + TSharedPtr PrefixRemoveBox; + TSharedPtr SuffixRemoveBox; +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Public/LintRule.h b/Source/Linter/Public/LintRule.h new file mode 100644 index 0000000..34baaf8 --- /dev/null +++ b/Source/Linter/Public/LintRule.h @@ -0,0 +1,107 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.generated.h" + +class ULintRule; +class ULintRuleSet; + +UENUM(BlueprintType) +enum class ELintRuleSeverity : uint8 { + Error, + Warning, + Info + // Ignore +}; + +USTRUCT(BlueprintType) +struct LINTER_API FLintRuleViolation { + GENERATED_USTRUCT_BODY() + + FLintRuleViolation() : + Violator(nullptr), + ViolatedRule(nullptr), + RecommendedAction(FText::GetEmpty()) { } + + FLintRuleViolation(UObject* InViolator, const TSubclassOf InViolatedRule, const FText InRecommendedAction = FText::GetEmpty()) : + Violator(InViolator), + ViolatedRule(InViolatedRule), + RecommendedAction(InRecommendedAction) { } + + // I don't particularly like this way of extracting relevant data, but alas here we are. + static TArray AllRuleViolationsWithViolator(const TArray& RuleViolationCollection, const UObject* SearchViolator); + static TArray> AllRuleViolationsWithViolatorShared(const TArray& RuleViolationCollection, const UObject* SearchViolator); + static TArray> AllRuleViolationsWithViolatorShared(const TArray>& RuleViolationCollection, const UObject* SearchViolator); + static TArray AllRuleViolationsOfSpecificRule(const TArray& RuleViolationCollection, TSubclassOf SearchRule); + static TArray AllRuleViolationsOfRuleGroup(const TArray& RuleViolationCollection, FName SearchRuleGroup); + + static TArray AllRuleViolationViolators(const TArray& RuleViolationCollection); + static TArray AllRuleViolationViolators(const TArray>& RuleViolationCollection); + static TMultiMap AllRuleViolationsMappedByViolator(const TArray& RuleViolationCollection); + static TMultiMap AllRuleViolationsMappedByViolatedLintRule(const TArray& RuleViolationCollection); + static TMultiMap> AllRuleViolationsMappedByViolatedLintRuleShared(const TArray& RuleViolationCollection); + static TMultiMap> AllRuleViolationsMappedByViolatedLintRuleShared(const TArray>& RuleViolationCollection); + + bool PopulateAssetData(); + + UPROPERTY(EditAnywhere, Category = "Lint") + TWeakObjectPtr Violator; + + UPROPERTY(EditAnywhere, Category = "Lint") + TSubclassOf ViolatedRule; + + UPROPERTY(EditAnywhere, Category = "Lint") + FText RecommendedAction; + + FAssetData ViolatorAssetData; +}; + +/** + *Comment + */ +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule : public UObject { + GENERATED_BODY() + +public: + ULintRule(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Display") + FName RuleGroup; + + UPROPERTY(EditDefaultsOnly, Category = "Display") + FText RuleTitle; + + UPROPERTY(EditDefaultsOnly, Category = "Display") + FText RuleDescription; + + UPROPERTY(EditDefaultsOnly, Category = "Display") + FString RuleURL; + + UPROPERTY(EditDefaultsOnly, Category = "Display") + ELintRuleSeverity RuleSeverity; + + UPROPERTY(EditDefaultsOnly, Category = "Settings", AdvancedDisplay) + bool bRequiresGameThread = false; + + UFUNCTION(BlueprintCallable, Category = "Lint") + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; + + UFUNCTION(BlueprintCallable, Category = "Display") + virtual bool IsRuleSuppressed() const; + + UFUNCTION(BlueprintNativeEvent, Category = "Display") + FName GetRuleBasedObjectVariantName(UObject* ObjectToLint) const; + +protected: + /* This is the function that child lint rules should override to perform the meat of the rule check + * You do not call this directly. Always call PassesRule. PassesRule forwards to the PassesRule_Internal ONLY IF + * data is valid and the rule is not suppressed, therefore it is worth checking. + */ + UFUNCTION(BlueprintNativeEvent, Category = "Lint") + bool PassesRule_Internal(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; + +private: +}; diff --git a/Source/Linter/Public/LintRuleSet.h b/Source/Linter/Public/LintRuleSet.h new file mode 100644 index 0000000..0541a47 --- /dev/null +++ b/Source/Linter/Public/LintRuleSet.h @@ -0,0 +1,90 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + +#include "LinterNamingConvention.h" +#include "Misc/ScopedSlowTask.h" +#include "LintRule.h" +#include "LintRuleSet.generated.h" + + +USTRUCT(BlueprintType) +struct LINTER_API FLintRuleList { + GENERATED_BODY() + + UPROPERTY(EditAnywhere, Category = Default) + TArray> LintRules; + + bool RequiresGameThread() const; + bool PassesRules(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; +}; + + +UCLASS(BlueprintType, Blueprintable) +class ULintResults : public UObject { + GENERATED_BODY() + +public: + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + FString LintRuleSet; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + int32 Warnings = 0; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + int32 Errors = 0; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + FText Result; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + TArray Paths; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + TArray CheckedAssets; + + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Lint") + TArray Violations; + + TArray> GetSharedViolations() const; + + TSharedPtr GenerateJsonReport() const; + + UFUNCTION(BlueprintCallable, Category = "Lint") + FString GenerateJsonReportString() const; + + UFUNCTION(BlueprintCallable, Category = "Lint") + FString GenerateHTML() const; +}; + + +UCLASS(BlueprintType, Blueprintable) +class LINTER_API ULintRuleSet : public UDataAsset { + GENERATED_BODY() + +public: + // UFUNCTION(BlueprintCallable, Category = "Conventions") + const FLintRuleList* GetLintRuleListForClass(TSoftClassPtr Class) const; + + UFUNCTION(BlueprintCallable, Category = "Conventions") + ULinterNamingConvention* GetNamingConvention() const; + + /** Invoke this with a list of asset paths to recursively lint all assets in paths. */ + // UFUNCTION(BlueprintCallable, Category = "Lint") + ULintResults* LintPath(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask = nullptr) const; + + UPROPERTY(EditDefaultsOnly, Category = "Marketplace") + bool bShowMarketplacePublishingInfoInLintWizard = false; + + UPROPERTY(EditDefaultsOnly, Category = "Rules") + FText RuleSetDescription; + + UPROPERTY(EditDefaultsOnly, Category = "Commandlet") + FString NameForCommandlet; + +protected: + UPROPERTY(EditDefaultsOnly, Category = "Rules") + TSoftObjectPtr NamingConvention; + + UPROPERTY(EditDefaultsOnly, Category = "Rules") + TMap, FLintRuleList> ClassLintRulesMap; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h new file mode 100644 index 0000000..6427acf --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h @@ -0,0 +1,23 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.generated.h" + +class ULintRuleSet; + +/** + *Comment + */ +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Base : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Base(const FObjectInitializer& ObjectInitializer); + + // This does rule pre-checks. You probably want to override PassesRule_Internal_Implementation + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h new file mode 100644 index 0000000..390268d --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h @@ -0,0 +1,22 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Compiles.generated.h" + +/** + *Comment + */ +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Compiles : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Compiles(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h new file mode 100644 index 0000000..b2c517e --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Funcs_MaxNodes.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Funcs_MaxNodes : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Funcs_MaxNodes(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + int32 MaxExpectedNonTrivialNodes = 50; + + static bool IsNodeTrivial(const UEdGraphNode* Node); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h new file mode 100644 index 0000000..337a3c0 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h @@ -0,0 +1,18 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Funcs_MustHaveReturn.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Funcs_MustHaveReturn : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Funcs_MustHaveReturn(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h new file mode 100644 index 0000000..74524fe --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h @@ -0,0 +1,18 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Funcs_PublicDescriptions.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Funcs_PublicDescriptions : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Funcs_PublicDescriptions(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h new file mode 100644 index 0000000..1975383 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h @@ -0,0 +1,19 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" + +#include "LintRule_Blueprint_LooseNodes.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_LooseNodes : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_LooseNodes(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h new file mode 100644 index 0000000..2757a13 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h @@ -0,0 +1,22 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Vars_ConfigCategories.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Vars_ConfigCategories : public ULintRule_Blueprint_Base { + GENERATED_BODY() + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + int32 NumVariablesToRequireCategorization = 5; + +public: + ULintRule_Blueprint_Vars_ConfigCategories(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h new file mode 100644 index 0000000..22e4249 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h @@ -0,0 +1,19 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" + +#include "LintRule_Blueprint_Vars_EditableMustHaveTooltip.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Vars_EditableMustHaveTooltip : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Vars_EditableMustHaveTooltip(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h new file mode 100644 index 0000000..4228897 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h @@ -0,0 +1,19 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" + +#include "LintRule_Blueprint_Vars_NoConfigFlag.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Vars_NoConfigFlag : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Vars_NoConfigFlag(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h new file mode 100644 index 0000000..6ce482a --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h @@ -0,0 +1,22 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" + +#include "LintRule_Blueprint_Vars_NonAtomic.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Vars_NonAtomic : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Vars_NonAtomic(const FObjectInitializer& ObjectInitializer); + + static bool IsVariableAtomic(FBPVariableDescription& VarDesc); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h new file mode 100644 index 0000000..ffc33a9 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h @@ -0,0 +1,19 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Vars_PluralArrays.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Vars_PluralArrays : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Vars_PluralArrays(const FObjectInitializer& ObjectInitializer); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h new file mode 100644 index 0000000..de93ba0 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h @@ -0,0 +1,28 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "LintRule.h" +#include "LintRule_Blueprint_Base.h" +#include "LintRule_Blueprint_Vars_Regex.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Blueprint_Vars_Regex : public ULintRule_Blueprint_Base { + GENERATED_BODY() + +public: + ULintRule_Blueprint_Vars_Regex(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + bool bUseLowercaseBPrefixForBooleans = true; + + UPROPERTY(EditAnywhere, Category = "Settings") + FString RegexPatternString; + + UPROPERTY(EditAnywhere, Category = "Settings") + bool bMustNotContainRegexPattern = true; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Collection.h b/Source/Linter/Public/LintRules/LintRule_Collection.h new file mode 100644 index 0000000..baa9cb3 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Collection.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_Collection.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Collection : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Collection(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + TArray> SubRules; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h b/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h new file mode 100644 index 0000000..a59bcb5 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "LintRule_IsNamedCorrectly_Base.generated.h" + + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_IsNamedCorrectly_Base : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_IsNamedCorrectly_Base(const FObjectInitializer& ObjectInitializer); + + UFUNCTION(BlueprintCallable, Category="Lint") + static FString BuildSuggestedName(FString CurrentName, FString DesiredPrefix, FString DesiredSuffix = TEXT("")); + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Level_LightingNeedsToRebuilt.h b/Source/Linter/Public/LintRules/LintRule_Level_LightingNeedsToRebuilt.h new file mode 100644 index 0000000..9503d52 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Level_LightingNeedsToRebuilt.h @@ -0,0 +1,21 @@ +// Copyright (c) 2021-2025, Forschungszentrum Jülich GmbH. All rights reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "LintRule.h" +#include "LintRule_Level_LightingNeedsToRebuilt.generated.h" + + +UCLASS(BlueprintType, Blueprintable) +class LINTER_API ULintRule_Level_LightingNeedsToRebuilt : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Level_LightingNeedsToRebuilt(const FObjectInitializer& ObjectInitializer); + + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h b/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h new file mode 100644 index 0000000..dd53ca6 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h @@ -0,0 +1,36 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_ParticleSystem_EmitterNameRegex.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_ParticleSystem_EmitterNameRegex : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_ParticleSystem_EmitterNameRegex(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditAnywhere, Category = "Settings") + int32 MinEmittersNeededToEnforce = 2; + + UPROPERTY(EditAnywhere, Category="Settings") + FString RegexPatternString; + + UPROPERTY(EditAnywhere, Category = "Settings") + bool bMustNotContainRegexPattern = true; + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + FText DisallowedRecommendedAction; + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + FText NonConformingRecommendedAction; + + + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h b/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h new file mode 100644 index 0000000..1ef8c68 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_Path_DisallowNames.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Path_DisallowNames : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Path_DisallowNames(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + TArray DisallowedFolderNames; + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + FText RecommendedAction; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h b/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h new file mode 100644 index 0000000..d58d5ed --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_Path_IsNotTooLong.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Path_IsNotTooLong : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Path_IsNotTooLong(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + int32 MaxPathLimit = 140; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h b/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h new file mode 100644 index 0000000..a74249c --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_Path_NoTopLevel.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Path_NoTopLevel : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Path_NoTopLevel(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Display") + FText ZeroTopLevelFoldersRecommendedAction; + + UPROPERTY(EditDefaultsOnly, Category = "Display") + FText PleaseUseThisFolderRecommendedAction; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Path_Regex.h b/Source/Linter/Public/LintRules/LintRule_Path_Regex.h new file mode 100644 index 0000000..02fd975 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Path_Regex.h @@ -0,0 +1,39 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_Path_Regex.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Path_Regex : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Path_Regex(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditAnywhere, Category="Settings") + FString RegexPatternString; + + UPROPERTY(EditAnywhere, Category = "Settings") + bool bMustNotContainRegexPattern = true; + + UPROPERTY(EditAnywhere, Category = "Settings") + bool bCheckPerPathElement = true; + + UPROPERTY(EditDefaultsOnly, Category = "Settings|Whole Path") + FText DisallowedWholePathRecommendedAction; + + UPROPERTY(EditDefaultsOnly, Category = "Settings|Whole Path") + FText NonConformingWholePathRecommendedAction; + + UPROPERTY(EditDefaultsOnly, Category = "Settings|Path Element") + FText DisallowedPathElementRecommendedAction; + + UPROPERTY(EditDefaultsOnly, Category = "Settings|Path Element") + FText NonConformingPathElementRecommendedAction; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h b/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h new file mode 100644 index 0000000..f2aebe0 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_SoundWave_SampleRate.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_SoundWave_SampleRate : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_SoundWave_SampleRate(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + TArray ValidSampleRates; + + + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h b/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h new file mode 100644 index 0000000..8f142b9 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h @@ -0,0 +1,23 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_StaticMesh_ValidUVs.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_StaticMesh_ValidUVs : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_StaticMesh_ValidUVs(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + bool bIgnoreMissingUVs = false; + + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h b/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h new file mode 100644 index 0000000..f0f0959 --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h @@ -0,0 +1,26 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" + +#include "LintRule_Texture_Size_NotTooBig.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Texture_Size_NotTooBig : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Texture_Size_NotTooBig(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + int32 MaxTextureSizeX = 8192; + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + int32 MaxTextureSizeY = 8192; + + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h b/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h new file mode 100644 index 0000000..804edea --- /dev/null +++ b/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + + +#include "LintRule.h" +#include "Engine/TextureDefines.h" + +#include "LintRule_Texture_Size_PowerOfTwo.generated.h" + +UCLASS(BlueprintType, Blueprintable, Abstract) +class LINTER_API ULintRule_Texture_Size_PowerOfTwo : public ULintRule { + GENERATED_BODY() + +public: + ULintRule_Texture_Size_PowerOfTwo(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditDefaultsOnly, Category = "Settings") + TSet> IgnoreTexturesInTheseGroups; + + virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; + +protected: + virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; +}; diff --git a/Source/Linter/Public/LintRunner.h b/Source/Linter/Public/LintRunner.h new file mode 100644 index 0000000..3cb7aca --- /dev/null +++ b/Source/Linter/Public/LintRunner.h @@ -0,0 +1,32 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + + +#include "HAL/Runnable.h" + +struct FLintRuleList; +struct FLintRuleViolation; +class ULintRuleSet; + +class FLintRunner : public FRunnable { +public: + FLintRunner(UObject* InLoadedObject, const ULintRuleSet* LintRuleSet, TArray* InpOutRuleViolations, FScopedSlowTask* InParentScopedSlowTask); + + virtual bool RequiresGamethread(); + + virtual bool Init() override; + virtual uint32 Run() override; + virtual void Stop() override; + virtual void Exit() override; + +protected: + UObject* LoadedObject = nullptr; + const ULintRuleSet* RuleSet = nullptr; + TArray* pOutRuleViolations; + + const FLintRuleList* pLoadedRuleList; + static FCriticalSection LintDataUpdateLock; + + FScopedSlowTask* ParentScopedSlowTask; +}; diff --git a/Source/Linter/Public/Linter.h b/Source/Linter/Public/Linter.h new file mode 100644 index 0000000..41202cc --- /dev/null +++ b/Source/Linter/Public/Linter.h @@ -0,0 +1,49 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + +#include "Widgets/Docking/SDockTab.h" +#include "Styling/SlateStyle.h" + + +class FLinterManagerBase; + + +DECLARE_LOG_CATEGORY_EXTERN(LogLinter, Verbose, All); +DECLARE_LOG_CATEGORY_EXTERN(LogCommandlet, All, All); + + +class LINTER_API FLinterModule : public IModuleInterface { +public: + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + static TSharedRef SpawnTab(const FSpawnTabArgs& TabSpawnArgs, TSharedPtr StyleSet); + + virtual bool SupportsDynamicReloading() override { + return false; + } + + virtual TArray GetDesiredLintPaths() { + if (DesiredLintPaths.Num() == 0) { + DesiredLintPaths.Push(TEXT("/Game")); + } + + return DesiredLintPaths; + } + + virtual void SetDesiredLintPaths(TArray LintPaths) { + DesiredLintPaths = LintPaths; + if (DesiredLintPaths.Num() == 0) { + DesiredLintPaths.Push(TEXT("/Game")); + } + } + +private: + TArray DesiredLintPaths; + +public: + void OnInitialAssetRegistrySearchComplete(); + static void TryToLoadAllLintRuleSets(); +}; diff --git a/Source/Linter/Public/LinterBase.h b/Source/Linter/Public/LinterBase.h new file mode 100644 index 0000000..fa5c9c9 --- /dev/null +++ b/Source/Linter/Public/LinterBase.h @@ -0,0 +1,102 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once +#include "Internationalization/Regex.h" + +#include "UObject/Object.h" + +/** Future Notes: + * The data structure for Linter was originally pretty straight forward. Now that we are introducing + * some 'smart' behavior to some of the errors i.e. arbitrary automated resolve actions + * and also tabulating data in both asset first and rule first orders, instead of being a + * set of loosely defined rules, rules should ideally become a strongly typed const table of some kind. + * This would simplify a lot of the data and structs being used here. -- Allar +**/ + +struct LINTER_API FLinterAssetError { + /** User friendly error message ready for displaying. */ + FText ErrorMessage; + + /** URL Link to link user to if they want more information on this error. */ + FString URLLink; + + FLinterAssetError(FText InErrorMessage, FString InURLLink = TEXT("")) : + ErrorMessage(InErrorMessage), + URLLink(InURLLink) { } +}; + +struct LINTER_API FLinterAssetErrorList { + FString AssetName; + FString AssetPath; + FString SuggestedAssetName; + TArray> Errors; + TArray> Warnings; + + + FLinterAssetErrorList() {}; + + FLinterAssetErrorList(FString InAssetName, FString InAssetPath, TArray> InErrors, TArray> InWarnings, FString InSuggestedAssetName = TEXT("")) : + AssetName(InAssetName), + AssetPath(InAssetPath), + SuggestedAssetName(InSuggestedAssetName), + Errors(InErrors), + Warnings(InWarnings) { } + + FLinterAssetErrorList(const UObject* Object, TArray> InErrors, TArray> InWarnings, FString InSuggestedAssetName = TEXT("")) : + AssetName(Object->GetFName()), + AssetPath(Object->GetPathName()), + SuggestedAssetName(InSuggestedAssetName), + Errors(InErrors), + Warnings(InWarnings) { } + + void Reset() { + AssetName.Empty(); + AssetPath.Empty(); + SuggestedAssetName.Empty(); + Errors.Empty(); + Warnings.Empty(); + } + + bool IsEmpty() { + return Errors.Num() == 0 && Warnings.Num() == 0; + } + + bool HasErrors() { + return Errors.Num() != 0; + } +}; + +/* Linter was originally built to store its data on a per-asset basis. + * After Epic's purchase, UI needed a way to store data on a per-rule basis. + * The following structs help out the LinterManager populate a per-rule list after Linting is complete. */ + +struct LINTER_API FLinterAssetInfo { + FString AssetName; + FString AssetPath; + FString SuggestedAssetName; + + FText RuleErrorContext; + + FLinterAssetInfo(const UObject* Object, FText InRuleErrorContext = FText::GetEmpty(), FString InSuggestedAssetName = TEXT("")) { + AssetName = Object->GetName(); + AssetPath = Object->GetPathName(); + SuggestedAssetName = InSuggestedAssetName; + } +}; + +struct LINTER_API FLinterRuleErrorList { + FText RuleMessage; + FString RuleURL; + bool bWarning; + TArray> AssetInfos; + + FLinterRuleErrorList() : + bWarning(false) { } + + FLinterRuleErrorList(FText InRuleMessage, FText InRuleContext, bool bInWarning, const UObject* Object, FString InRuleURL, FString InSuggestedAssetName = TEXT("")) : + RuleMessage(InRuleMessage), + RuleURL(InRuleURL), + bWarning(bInWarning) { + AssetInfos.Add(MakeShareable(new FLinterAssetInfo(Object, InRuleContext, InSuggestedAssetName))); + } +}; diff --git a/Source/Linter/Public/LinterCommandlet.h b/Source/Linter/Public/LinterCommandlet.h new file mode 100644 index 0000000..7bb180b --- /dev/null +++ b/Source/Linter/Public/LinterCommandlet.h @@ -0,0 +1,11 @@ +// Copyright 2016 Gamemakin LLC. All Rights Reserved. + +#pragma once +#include "Commandlets/Commandlet.h" +#include "LinterCommandlet.generated.h" + +UCLASS() +class ULinterCommandlet : public UCommandlet { + GENERATED_UCLASS_BODY() + virtual int32 Main(const FString& Params) override; +}; diff --git a/Source/Linter/Public/LinterContentBrowserExtensions.h b/Source/Linter/Public/LinterContentBrowserExtensions.h new file mode 100644 index 0000000..d027d4c --- /dev/null +++ b/Source/Linter/Public/LinterContentBrowserExtensions.h @@ -0,0 +1,12 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + +class FLinterModule; + +// Integrate Linter actions into the Content Browser +class FLinterContentBrowserExtensions { +public: + static void InstallHooks(); + static void RemoveHooks(); +}; diff --git a/Source/Linter/Public/LinterNamingConvention.h b/Source/Linter/Public/LinterNamingConvention.h new file mode 100644 index 0000000..c83864d --- /dev/null +++ b/Source/Linter/Public/LinterNamingConvention.h @@ -0,0 +1,78 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once + +#include "UObject/Object.h" +#include "Templates/SharedPointer.h" +#include "IDetailCustomization.h" +#include "PropertyHandle.h" +#include "Engine/DataAsset.h" +#include "Misc/EngineVersionComparison.h" +#include "LinterNamingConvention.generated.h" + + +/** + * Class/Prefix/Suffix settings for Linter + */ +USTRUCT(BlueprintType) +struct LINTER_API FLinterNamingConventionInfo { + GENERATED_USTRUCT_BODY() + + FLinterNamingConventionInfo() : + SoftClassPtr(nullptr) {} + + FLinterNamingConventionInfo(TSoftClassPtr InClass, FString InPrefix = TEXT(""), FString InSuffix = TEXT(""), FName InVariant = NAME_None) : + SoftClassPtr(InClass), + Prefix(InPrefix), + Suffix(InSuffix), + Variant(InVariant) {} + + UPROPERTY(EditAnywhere, Category = Default, meta = (AllowAbstract = "")) + TSoftClassPtr SoftClassPtr; + + UPROPERTY(EditAnywhere, Category = Default) + FString Prefix; + + UPROPERTY(EditAnywhere, Category = Default) + FString Suffix; + + UPROPERTY(EditAnywhere, Category = Default) + FName Variant; +}; + +class FLinterNamingConventionDetails : public IDetailCustomization { +public: + /** Makes a new instance of this detail layout class for a specific detail view requesting it */ + static TSharedRef MakeInstance(); + + /** ILayoutDetails interface */ + virtual void CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) override; + + void OnGenerateElementForDetails(TSharedRef StructProperty, int32 ElementIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout); +}; + +/** +* Contains a naming convention to be used by LinterManagers/LinterRules +*/ +UCLASS(Abstract) +class LINTER_API ULinterNamingConvention : public UDataAsset { + GENERATED_BODY() + +public: + ULinterNamingConvention(const FObjectInitializer& ObjectInitializer); + + UPROPERTY(EditAnywhere, Category="Conventions", meta = (AllowAbstract = "")) + TArray ClassNamingConventions; + + UFUNCTION(BlueprintCallable, Category="Conventions") + TArray GetNamingConventionsForClassVariant(TSoftClassPtr Class, FName Variant = NAME_None) const; + + UFUNCTION(Blueprintcallable, Category = "Conventions") + void SortConventions(); + +#if UE_VERSION_NEWER_THAN(5, 0, 0) + virtual void PreSave(FObjectPreSaveContext ObjectSaveContext) override; +#else + virtual void PreSave(const class ITargetPlatform* TargetPlatform) override; +#endif +}; diff --git a/Plugins/Linter/Source/Linter/Public/LinterSettings.h b/Source/Linter/Public/LinterSettings.h similarity index 53% rename from Plugins/Linter/Source/Linter/Public/LinterSettings.h rename to Source/Linter/Public/LinterSettings.h index 5b13fac..6da5a39 100644 --- a/Plugins/Linter/Source/Linter/Public/LinterSettings.h +++ b/Source/Linter/Public/LinterSettings.h @@ -1,23 +1,22 @@ // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. #pragma once -#include "CoreMinimal.h" + + #include "UObject/Object.h" #include "LintRuleSet.h" #include "LinterSettings.generated.h" - /** * Implements the settings for the Linter plugin. */ UCLASS(config = Linter, defaultconfig) -class ULinterSettings : public UObject -{ - GENERATED_UCLASS_BODY() - -public: +class ULinterSettings : public UObject { + GENERATED_BODY() - UPROPERTY(EditAnywhere, config, Category = Settings) - TAssetPtr DefaultLintRuleSet; + ULinterSettings(const FObjectInitializer& ObjectInitializer); +public: + UPROPERTY(EditAnywhere, config, Category = Settings) + TSoftObjectPtr DefaultLintRuleSet; }; diff --git a/Source/Linter/Public/LinterStyle.h b/Source/Linter/Public/LinterStyle.h new file mode 100644 index 0000000..b758c6d --- /dev/null +++ b/Source/Linter/Public/LinterStyle.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. + +#pragma once +#include "CoreTypes.h" +#include "Styling/ISlateStyle.h" + +class FLinterStyle { +public: + static void Initialize(); + + static void Shutdown(); + + static TSharedPtr Get(); + static TSharedPtr StyleSet; + + static FName GetStyleSetName(); + +private: + static FString InContent(const FString& RelativePath, const ANSICHAR* Extension); +}; diff --git a/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h b/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h new file mode 100644 index 0000000..18be9fe --- /dev/null +++ b/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Internationalization/Regex.h" + + +/** +* Helper struct for showing function tooltip widgets +**/ +struct FBPFunctionArgumentDescription { + FText ArgumentName; + FText Tooltip; + FText ArgumentType; + + FBPFunctionArgumentDescription() { } + + FBPFunctionArgumentDescription(FText InArgumentName, FText InTooltip, FText InArgumentType = FText::GetEmpty()) : + ArgumentName(InArgumentName), + Tooltip(InTooltip), + ArgumentType(InArgumentType) { } + + FText GetToolTipTextRef() { + return Tooltip; + } +}; + +class FTooltipStringHelper { +public: + static FText ParseFunctionRawTooltipGetDescription(FString RawTooltip, bool bRemoveNewlines = false); + static bool ParseFunctionRawTooltip(FString RawTooltip, FText& OutFunctionDescription, TArray>& Inputs, TArray>& Outputs, FText& OutReturnText); + static FString ConvertTooltipDataToRawTooltip(FText FunctionDescription, TArray> Inputs, TArray> Outputs); + static bool FindAndUpdateArgumentTooltip(FText ArgumentName, FText Tooltip, TArray>& Arguments); +}; diff --git a/Source/Linter/Public/TooltipEditor/TooltipTool.h b/Source/Linter/Public/TooltipEditor/TooltipTool.h new file mode 100644 index 0000000..806ab09 --- /dev/null +++ b/Source/Linter/Public/TooltipEditor/TooltipTool.h @@ -0,0 +1,138 @@ +// Copyright 2016 Gamemakin LLC. All Rights Reserved. + +#pragma once + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/SWindow.h" +#include "Widgets/Input/SCheckBox.h" +#include "Widgets/Input/SComboBox.h" +#include "Widgets/Input/SMultiLineEditableTextBox.h" +#include "Widgets/Views/SListView.h" +#include "AssetRegistry/AssetData.h" +#include "Engine/Blueprint.h" +#include "K2Node_FunctionEntry.h" +#include "K2Node_FunctionResult.h" + +#include "TooltipStringHelper.h" + +#define LOCTEXT_NAMESPACE "LinterTooltipTool" + + +/** +* Helper struct for showing function tooltip widgets +**/ +struct FBPFunctionPointers { + UK2Node_FunctionEntry* FunctionEntryNode; + UK2Node_FunctionResult* FunctionResultNode; + FName FunctionName; + + FBPFunctionPointers() : + FunctionEntryNode{nullptr}, + FunctionResultNode{nullptr} {} + + FBPFunctionPointers(UK2Node_FunctionEntry* InFunctionEntryNode, UK2Node_FunctionResult* InFunctionResultNode, FName InFunctionName) : + FunctionEntryNode(InFunctionEntryNode), + FunctionResultNode(InFunctionResultNode), + FunctionName(InFunctionName) { } +}; + +/** +* FTooltipTool +* +* Wrapper class for STooltipTool. This class creates and launches a dialog then awaits the +* result to return to the user. +*/ +class FTooltipTool { +public: + enum EResult { + Cancel = 0, + // No/Cancel, normal usage would stop the current action + Confirm = 1, + // Yes/Ok/Etc, normal usage would continue with action + }; + + FTooltipTool(const TArray Assets); + + /** Shows the dialog box and waits for the user to respond. */ + EResult ShowModal(); + + TArray BlueprintsInternal; + TArray> Blueprints; + +private: + /** Cached pointer to the modal window */ + TSharedPtr DialogWindow; + + /** Cached pointer to the batch rename tool widget */ + TSharedPtr DialogWidget; +}; + +/** +* Slate panel for batch renaming +*/ +class STooltipTool : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(STooltipTool) {} + /** Window in which this widget resides */ + SLATE_ATTRIBUTE(TSharedPtr, ParentWindow) + SLATE_ATTRIBUTE(TArray>, Blueprints) + SLATE_END_ARGS() + + /** + * Constructs this widget + * + * @param InArgs The declaration data for this widget + */ + void Construct(const FArguments& InArgs); + + /** + * Returns the EResult of the button which the user pressed. Closing of the dialog + * in any other way than clicking "Ok" results in this returning a "Cancel" value + */ + FTooltipTool::EResult GetUserResponse() const; + +private: + /** + * Handles when a button is pressed, should be bound with appropriate EResult Key + * + * @param ButtonID - The return type of the button which has been pressed. + */ + FReply OnButtonClick(FTooltipTool::EResult ButtonID); + + FText GetSelectedBlueprintText() const; + + void UpdateVariableTooltipText(const FText& NewText); + void UpdateCurrentFunctionTooltipText(); + + void RebuildMemberList(); + + /** Stores the users response to this dialog */ + FTooltipTool::EResult UserResponse; + + /** Pointer to the window which holds this Widget, required for modal control */ + TSharedPtr ParentWindow; + +public: + TAttribute>> Blueprints; + TSharedPtr>> BlueprintComboBox; + + FText CurrentFunctionDescription; + + TArray> FunctionPointers; + TSharedPtr>> FunctionListView; + TSharedPtr FunctionDescriptionTooltipBox; + TArray> FunctionArgumentDescriptions; + TSharedPtr>> FunctionArgumentListView; + TArray> FunctionOutputDescriptions; + TSharedPtr>> FunctionOutputListView; + + + TArray> Members; + TSharedPtr>> MemberListView; + TSharedPtr VariableTooltipEditableTextBox; + + TSharedPtr CommitTextButton; + TSharedPtr CommitOnTextChangeCheckBox; +}; + +#undef LOCTEXT_NAMESPACE diff --git a/Source/Linter/Public/UI/LintReport.h b/Source/Linter/Public/UI/LintReport.h new file mode 100644 index 0000000..8d1f7ed --- /dev/null +++ b/Source/Linter/Public/UI/LintReport.h @@ -0,0 +1,37 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/Layout/SScrollBox.h" + +#include "LintRule.h" + + +class ULintResults; + + +class SLintReport : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReport) { } + + SLATE_END_ARGS() + +public: + void Construct(const FArguments& Args); + void Rebuild(const ULintRuleSet* SelectedLintRuleSet); + TSharedRef GetViewButtonContent(); + + const ULintRuleSet* LastUsedRuleSet = nullptr; + + TSharedPtr ResultsTextBlockPtr; + TSharedPtr ViewOptionsComboButton; + TSharedPtr AssetDetailsScrollBoxPtr; + TSharedPtr RuleDetailsScrollBoxPtr; + + ULintResults* LintResults = nullptr; + TArray> RuleViolations; + FString JsonReport; + FString HTMLReport; + + bool bHasRanReport = false; +}; diff --git a/Source/Linter/Public/UI/LintReportAssetDetails.h b/Source/Linter/Public/UI/LintReportAssetDetails.h new file mode 100644 index 0000000..96051a9 --- /dev/null +++ b/Source/Linter/Public/UI/LintReportAssetDetails.h @@ -0,0 +1,21 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once +#include "Widgets/SCompoundWidget.h" +#include "LintRule.h" + +class SLintReportAssetDetails : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReportAssetDetails) { } + SLATE_ATTRIBUTE(FAssetData, AssetData) + SLATE_ATTRIBUTE(TArray>, RuleViolations) + SLATE_ATTRIBUTE(TSharedPtr, ThumbnailPool) + + SLATE_END_ARGS() + + TAttribute AssetData; + TAttribute>> RuleViolations; + TAttribute> ThumbnailPool; + +public: + void Construct(const FArguments& Args); +}; diff --git a/Source/Linter/Public/UI/LintReportAssetError.h b/Source/Linter/Public/UI/LintReportAssetError.h new file mode 100644 index 0000000..e55259e --- /dev/null +++ b/Source/Linter/Public/UI/LintReportAssetError.h @@ -0,0 +1,17 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once +#include "Widgets/SCompoundWidget.h" +#include "LintRule.h" + +class SLintReportAssetError : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReportAssetError) { } + SLATE_ATTRIBUTE(TSharedPtr, RuleViolation) + + SLATE_END_ARGS() + + TAttribute> RuleViolation; + +public: + void Construct(const FArguments& Args); +}; diff --git a/Source/Linter/Public/UI/LintReportAssetErrorList.h b/Source/Linter/Public/UI/LintReportAssetErrorList.h new file mode 100644 index 0000000..32673f4 --- /dev/null +++ b/Source/Linter/Public/UI/LintReportAssetErrorList.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + +struct FLintRuleViolation; + +class SLintReportAssetErrorList : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReportAssetErrorList) { } + SLATE_ATTRIBUTE(TArray>, RuleViolations) + + SLATE_END_ARGS() + + TAttribute>> RuleViolations; + +public: + void Construct(const FArguments& Args); +}; diff --git a/Source/Linter/Public/UI/LintReportRuleDetails.h b/Source/Linter/Public/UI/LintReportRuleDetails.h new file mode 100644 index 0000000..6094ee0 --- /dev/null +++ b/Source/Linter/Public/UI/LintReportRuleDetails.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once +#include "Widgets/SCompoundWidget.h" +#include "LintRule.h" + +class SLintReportRuleDetails : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReportRuleDetails) { } + SLATE_ATTRIBUTE(TArray>, RuleViolations) + SLATE_ATTRIBUTE(TSharedPtr, ThumbnailPool) + + SLATE_END_ARGS() + + TAttribute>> RuleViolations; + TAttribute> ThumbnailPool; + +public: + void Construct(const FArguments& Args); + +private: + FString RuleURL; + FAssetData RuleAssetData; + TSharedPtr ThumbnailBox; +}; diff --git a/Source/Linter/Public/UI/LintReportRuleError.h b/Source/Linter/Public/UI/LintReportRuleError.h new file mode 100644 index 0000000..7e8df5a --- /dev/null +++ b/Source/Linter/Public/UI/LintReportRuleError.h @@ -0,0 +1,18 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + +#include "Widgets/SCompoundWidget.h" +#include "LintRule.h" + +class SLintReportRuleError : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReportRuleError) { } + SLATE_ATTRIBUTE(TSharedPtr, RuleViolation) + + SLATE_END_ARGS() + + TAttribute> RuleViolation; + +public: + void Construct(const FArguments& Args); +}; diff --git a/Source/Linter/Public/UI/LintReportRuleErrorList.h b/Source/Linter/Public/UI/LintReportRuleErrorList.h new file mode 100644 index 0000000..961724f --- /dev/null +++ b/Source/Linter/Public/UI/LintReportRuleErrorList.h @@ -0,0 +1,20 @@ +// Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. +#pragma once + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/DeclarativeSyntaxSupport.h" + +struct FLintRuleViolation; + +class SLintReportRuleErrorList : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintReportRuleErrorList) { } + SLATE_ATTRIBUTE(TArray>, RuleViolations) + + SLATE_END_ARGS() + + TAttribute>> RuleViolations; + +public: + void Construct(const FArguments& Args); +}; diff --git a/Source/Linter/Public/UI/LintWizard.h b/Source/Linter/Public/UI/LintWizard.h new file mode 100644 index 0000000..48d46ae --- /dev/null +++ b/Source/Linter/Public/UI/LintWizard.h @@ -0,0 +1,49 @@ +// Copyright 2015-2017 by Gamemakin LLC + +#pragma once + + +#include "Widgets/SCompoundWidget.h" +#include "Widgets/Workflow/SWizard.h" +#include "UI/SStepWidget.h" +#include "LintReport.h" + + +class LINTER_API SLintWizard : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SLintWizard) {} + SLATE_END_ARGS() + + /** Constructs this widget with InArgs */ + void Construct(const FArguments& InArgs); + + /** The wizard widget */ + TSharedPtr MainWizard; + + /** The Lint Report widget */ + TSharedPtr LintReport; + + /** The Linter combo box selection to use to select a linter rule set. */ + TSharedPtr>> RuleSetSelectionComboBox; + + /** List of Linter managers grabbed from the Linter Module on widget creation */ + TArray> RuleSets; + + /** List of maps in the project used for wizard purposes */ + TArray> MapAssetDataList; + + /** Scrollbox for list of map assets in marketplace recommendation page */ + TSharedPtr MarketplaceRecommendationMapScrollBoxPtr; + + /** Currently selected Linter Rule Set */ + TSharedPtr SelectedRuleSet; + + bool bOfferPackage = false; + EStepStatus FixUpRedirectorStatus = Unknown; + EStepStatus SaveAllStatus = Unknown; + + void OnLintReportEntered(); + void OnMarketplaceRecommendationsEntered(); + + bool LoadAssetsIfNeeded(const TArray& ObjectPaths, TArray& LoadedObjects); +}; diff --git a/Source/Linter/Public/UI/SAssetLinkWidget.h b/Source/Linter/Public/UI/SAssetLinkWidget.h new file mode 100644 index 0000000..629fa9d --- /dev/null +++ b/Source/Linter/Public/UI/SAssetLinkWidget.h @@ -0,0 +1,19 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SCompoundWidget.h" + +class SAssetLinkWidget : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SAssetLinkWidget) { } + + SLATE_ATTRIBUTE(FAssetData, AssetData) + + SLATE_END_ARGS() + + TAttribute AssetData; + +public: + void Construct(const FArguments& Args); +}; diff --git a/Source/Linter/Public/UI/SStepWidget.h b/Source/Linter/Public/UI/SStepWidget.h new file mode 100644 index 0000000..1d0a4d9 --- /dev/null +++ b/Source/Linter/Public/UI/SStepWidget.h @@ -0,0 +1,61 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#pragma once + +#include "Widgets/DeclarativeSyntaxSupport.h" +#include "Widgets/SCompoundWidget.h" +#include "Misc/ScopedSlowTask.h" + + +enum EStepStatus { + NoStatus, + Unknown, + InProgress, + NeedsUpdate, + Warning, + Error, + Success +}; + +// Called when a step's action is invoked +DECLARE_DELEGATE_OneParam(FOnStepPerformAction, FScopedSlowTask&); + +/* Widget that represents a 'step' or 'choice' in the PaCK wizard. */ +class SStepWidget : public SCompoundWidget { +public: + SLATE_BEGIN_ARGS(SStepWidget) : + _StepStatus(NoStatus), + _ShowStepStatusIcon(true) { } + + /** Name to display for this step. */ + SLATE_ATTRIBUTE(FText, StepName) + + /** Description to display for this step. */ + SLATE_ATTRIBUTE(FText, StepDesc) + + /** Text to display within the action button for this step. */ + SLATE_ATTRIBUTE(FText, StepActionText) + + /** Slate Brush to use as the thumbnail icon for this step. */ + SLATE_ATTRIBUTE(const FSlateBrush*, Icon) + + /** Current status for this step. */ + SLATE_ATTRIBUTE(EStepStatus, StepStatus) + + /** Current status for this step. */ + SLATE_ATTRIBUTE(bool, ShowStepStatusIcon) + + /** Delegate to fire when this step's action is invoked. */ + SLATE_EVENT(FOnStepPerformAction, OnPerformAction) + + SLATE_END_ARGS() + +public: + TAttribute StepStatus; + TAttribute StepActionText; + FOnStepPerformAction OnPerformAction; + TAttribute ShowStepStatusIcon; + + bool IsStepCompleted(bool bAllowWarning = true) const; + + void Construct(const FArguments& Args); +}; diff --git a/Source/MarketplaceLinter/MarketplaceLinter.Build.cs b/Source/MarketplaceLinter/MarketplaceLinter.Build.cs new file mode 100644 index 0000000..39075ef --- /dev/null +++ b/Source/MarketplaceLinter/MarketplaceLinter.Build.cs @@ -0,0 +1,16 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class MarketplaceLinter : ModuleRules { + public MarketplaceLinter(ReadOnlyTargetRules Target) : base(Target) { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new[] { "Core", "Linter" }); + PrivateDependencyModuleNames.AddRange(new[] { + "CoreUObject", "Engine", "Slate", "SlateCore", "RenderCore", "UnrealEd", "GraphEditor", "AssetTools", + "EditorStyle", "Projects", "BlueprintGraph", "InputCore", "StandaloneRenderer", "PropertyEditor", + "LevelEditor", "LauncherPlatform", "AppFramework", "DesktopPlatform", "UATHelper" + }); + } +} \ No newline at end of file diff --git a/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp b/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp new file mode 100644 index 0000000..8de877a --- /dev/null +++ b/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp @@ -0,0 +1,7 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#include "MarketplaceLinter.h" + + +IMPLEMENT_MODULE(FDefaultModuleImpl, MarketplaceLinter) +DEFINE_LOG_CATEGORY(LogMarketplaceLinter); diff --git a/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp b/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp new file mode 100644 index 0000000..794cfaf --- /dev/null +++ b/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp @@ -0,0 +1,108 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. +#include "MarketplaceNamingConvention.h" + + +UMarketplaceNamingConvention::UMarketplaceNamingConvention(const FObjectInitializer& ObjectInitializer) : + Super(ObjectInitializer) { +#define SCRIPT_PATH(ScriptPath) TSoftClassPtr(FSoftObjectPath(TEXT("/Script/" #ScriptPath))) +#define ADD_PREFIX(ClassName, Prefix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix)); +#define ADD_PREFIX_SUFFIX(ClassName, Prefix, Suffix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix, Suffix)); + + // Animation + ADD_PREFIX(AimOffsetBlendSpace, "AO_"); + ADD_PREFIX(AimOffsetBlendSpace1D, "AO_"); + ADD_PREFIX(AnimBlueprint, "ABP_"); + ADD_PREFIX(AnimComposite, "AC_"); + ADD_PREFIX(AnimMontage, "AM_"); + ADD_PREFIX(AnimSequence, "A_"); + ADD_PREFIX(BlendSpace, "BS_"); + ADD_PREFIX(BlendSpace1D, "BS_"); + ADD_PREFIX(MorphTarget, "MT_"); + ADD_PREFIX(Rig, "Rig_"); + ADD_PREFIX(SkeletalMesh, "SK_"); + ADD_PREFIX(Skeleton, "SKEL_"); + + + // Artificial Intelligence + ADD_PREFIX(AIController, "AIC_"); + ADD_PREFIX(BehaviorTree, "BT_"); + ADD_PREFIX(BlackboardData, "BB_"); + ADD_PREFIX(BTDecorator, "BTDecorator_"); + ADD_PREFIX(BTService, "BTService_"); + ADD_PREFIX(BTTaskNode, "BTTask_"); + + // Blueprints + ADD_PREFIX(Blueprint, "BP_"); + ADD_PREFIX(BlueprintFunctionLibrary, "BPFL_"); + ADD_PREFIX(Interface, "BPI_"); + ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("IntroTutorials.EditorTutorial"), "TBP_")); + ADD_PREFIX(UserDefinedEnum, "E"); + ADD_PREFIX(UserDefinedStruct, "F"); + + // Materials + ADD_PREFIX(Material, "M_"); + ADD_PREFIX(Material, "MA_"); + ADD_PREFIX(Material, "MAT_"); + ADD_PREFIX(MaterialFunction, "MF_"); + ADD_PREFIX(MaterialInstance, "MI_"); + ADD_PREFIX(MaterialInstanceConstant, "MI_"); + ADD_PREFIX(MaterialParameterCollection, "MPC_"); + ADD_PREFIX(SubsurfaceProfile, "SP_"); + + // Textures + ADD_PREFIX(Texture2D, "T_"); + ADD_PREFIX(TextureCube, "TC_"); + ADD_PREFIX(TextureRenderTarget2D, "RT_"); + ADD_PREFIX(TextureRenderTargetCube, "RTC_"); + ADD_PREFIX(TextureLightProfile, "TLP_"); + + // Media + ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaTexture"), "MT_")); + ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaPlayer"), "MP_")); + + // Miscellaneous + ADD_PREFIX(VectorFieldAnimated, "VFA_"); + ADD_PREFIX(CameraAnim, "CA_"); + ADD_PREFIX(CurveLinearColor, "Curve_"); + ADD_PREFIX(CurveTable, "Curve_"); + ADD_PREFIX(DataTable, "DT_"); + ADD_PREFIX(CurveFloat, "Curve_"); + ADD_PREFIX(ForceFeedbackEffect, "FFE_"); + ADD_PREFIX(MatineeAnimInterface, "Matinee_"); + ADD_PREFIX(ObjectLibrary, "OL_"); + ADD_PREFIX(VectorFieldStatic, "VF_"); + ADD_PREFIX(TouchInterface, "TI_"); + ADD_PREFIX(CurveVector, "Curve_"); + ADD_PREFIX(StaticMesh, "SM_"); + ADD_PREFIX(StaticMesh, "S_"); + + // Paper 2D + + // Physics + ADD_PREFIX(PhysicalMaterial, "PM_"); + ADD_PREFIX(PhysicsAsset, "PHYS_"); + + // Sounds + ADD_PREFIX(DialogueVoice, "DV_"); + ADD_PREFIX(DialogueWave, "DW_"); + ADD_PREFIX(ReverbEffect, "Reverb_"); + ADD_PREFIX(SoundAttenuation, "ATT_"); + ADD_PREFIX(SoundClass, ""); + ADD_PREFIX(SoundConcurrency, "_SC"); + ADD_PREFIX_SUFFIX(SoundCue, "A_", "_Cue"); + ADD_PREFIX(SoundMix, "Mix_"); + ADD_PREFIX(SoundWave, "A_"); + + // User Interface + ADD_PREFIX(Font, "Font_"); + ADD_PREFIX(SlateBrushAsset, "Brush_"); + ADD_PREFIX(SlateWidgetStyleAsset, "Style_"); + ADD_PREFIX(WidgetBlueprint, "WBP_"); + + // Effects + ADD_PREFIX(ParticleSystem, "PS_"); + +#undef ADD_PREFIX + + SortConventions(); +} diff --git a/Source/MarketplaceLinter/Public/MarketplaceLinter.h b/Source/MarketplaceLinter/Public/MarketplaceLinter.h new file mode 100644 index 0000000..b704d72 --- /dev/null +++ b/Source/MarketplaceLinter/Public/MarketplaceLinter.h @@ -0,0 +1,5 @@ +// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. + +#pragma once + +DECLARE_LOG_CATEGORY_EXTERN(LogMarketplaceLinter, Verbose, All); diff --git a/Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h b/Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h similarity index 56% rename from Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h rename to Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h index 55827fe..1cc86fc 100644 --- a/Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h +++ b/Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h @@ -1,20 +1,15 @@ // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. #pragma once -#include "CoreMinimal.h" -#include "UObject/Object.h" -#include "Templates/SharedPointer.h" + #include "LinterNamingConvention.h" #include "MarketplaceNamingConvention.generated.h" UCLASS() -class UMarketplaceNamingConvention : public ULinterNamingConvention -{ - GENERATED_BODY() +class UMarketplaceNamingConvention : public ULinterNamingConvention { + GENERATED_BODY() public: - - UMarketplaceNamingConvention(const FObjectInitializer& ObjectInitializer); - + UMarketplaceNamingConvention(const FObjectInitializer& ObjectInitializer); };