├── docs ├── img │ ├── LintReport.png │ ├── LintRulesMap.png │ ├── LintRulesInBP.png │ ├── LinterLauncher.png │ ├── ScanWithLinter.png │ ├── ShowPluginContent.png │ ├── MarketplaceLintRuleSet.png │ └── MarketplaceLinterFolder.png ├── style.md ├── gettingstarted.md ├── index.md └── unrealguidelines.md ├── Plugins └── Linter │ ├── Resources │ └── Icon128.png │ ├── Content │ ├── Icons │ │ ├── icon_MapCheck_64x.png │ │ ├── icon_AddContent_64x.png │ │ ├── icon_Help_epic_16x.png │ │ ├── icon_Texture_Run_16x.png │ │ ├── icon_file_saveall_64x.png │ │ ├── icon_tab_Toolbars_64x.png │ │ ├── CompileStatus_Fail_64px.png │ │ ├── CompileStatus_Good_64px.png │ │ ├── icon_MessageLog_Info_14x.png │ │ ├── CompileStatus_Unknown_64px.png │ │ ├── CompileStatus_Warning_64px.png │ │ ├── CompileStatus_Working_64px.png │ │ ├── icon_Help_Documentation_16x.png │ │ ├── icon_MessageLog_Error_14x.png │ │ └── icon_MessageLog_Warning_14x.png │ ├── GamemakinLinter │ │ ├── GamemakinLinterRuleSet.uasset │ │ ├── LintRules │ │ │ ├── GMLR_Path_Maps.uasset │ │ │ ├── GMLR_BP_Compiles.uasset │ │ │ ├── GMLR_PathChecks.uasset │ │ │ ├── GMLR_BP_Vars_NoIs.uasset │ │ │ ├── GMLR_Path_NoSpaces.uasset │ │ │ ├── GMLR_Path_NoUnicode.uasset │ │ │ ├── GMLR_BP_NoConfigFlag.uasset │ │ │ ├── GMLR_IsNamedCorrectly.uasset │ │ │ ├── GMLR_Path_PascalCase.uasset │ │ │ ├── GMLR_BP_Funcs_MaxNodes.uasset │ │ │ ├── GMLR_BP_Funcs_MustReturn.uasset │ │ │ ├── GMLR_BP_Vars_NonAtomic.uasset │ │ │ ├── GMLR_BP_Vars_PascalCase.uasset │ │ │ ├── GMLR_Path_RedundantNames.uasset │ │ │ ├── GMLR_StaticMesh_ValidUVs.uasset │ │ │ ├── GMLR_Path_DoNotNameAssets.uasset │ │ │ ├── GMLR_Path_NoTopLevelAssets.uasset │ │ │ ├── GMLR_Texture2D_PowerOfTwo.uasset │ │ │ ├── GMLR_BP_Vars_ConfigCategories.uasset │ │ │ ├── GMLR_BP_Vars_EditableTooltips.uasset │ │ │ ├── GMLR_BP_Vars_NoPluralArrays.uasset │ │ │ ├── GMLR_Particles_EmitterNames.uasset │ │ │ ├── GMLR_Texture2D_Size_NotTooBig.uasset │ │ │ └── GMLR_BP_Funcs_PublicDescriptions.uasset │ │ └── GamemakinNamingConvention.uasset │ └── MarketplaceLinter │ │ ├── MarketplaceLintRuleSet.uasset │ │ ├── LintRules │ │ ├── MPLR_PathRules.uasset │ │ ├── MPLR_IsNamedCorrectly.uasset │ │ ├── MPLR_Path_AlphaNumeric.uasset │ │ ├── MPLR_Path_IsNotTooLong.uasset │ │ ├── MPLR_Path_PascalCase.uasset │ │ ├── MPLR_Blueprint_Compiles.uasset │ │ ├── MPLR_StaticMesh_ValidUVs.uasset │ │ ├── MPLR_Blueprint_LooseNodes.uasset │ │ ├── MPLR_Particles_EmitterNames.uasset │ │ ├── MPLR_Path_NoTopLevelAssets.uasset │ │ ├── MPLR_SoundWave_SampleRate.uasset │ │ ├── MPLR_Texture2D_PowerOfTwo.uasset │ │ ├── MPLR_Path_DisallowedPathNames.uasset │ │ └── MPLR_Texture2D_Size_NotTooBig.uasset │ │ └── MarketplaceNamingConvention.uasset │ ├── Source │ ├── Linter │ │ ├── Private │ │ │ ├── AnyObject_LinterDummyClass.cpp │ │ │ ├── LinterSettings.cpp │ │ │ ├── LintRules │ │ │ │ ├── LintRule_Blueprint_Base.cpp │ │ │ │ ├── LintRule_Path_IsNotTooLong.cpp │ │ │ │ ├── LintRule_Blueprint_Compiles.cpp │ │ │ │ ├── LintRule_Collection.cpp │ │ │ │ ├── LintRule_Path_DisallowNames.cpp │ │ │ │ ├── LintRule_Blueprint_Vars_NoConfigFlag.cpp │ │ │ │ ├── LintRule_Blueprint_Vars_PluralArrays.cpp │ │ │ │ ├── LintRule_SoundWave_SampleRate.cpp │ │ │ │ ├── LintRule_Texture_Size_NotTooBig.cpp │ │ │ │ ├── LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp │ │ │ │ ├── LintRule_Blueprint_LooseNodes.cpp │ │ │ │ ├── LintRule_Blueprint_Vars_NonAtomic.cpp │ │ │ │ ├── LintRule_Blueprint_Vars_RegEx.cpp │ │ │ │ ├── LintRule_Blueprint_Funcs_MustHaveReturn.cpp │ │ │ │ ├── LintRule_StaticMesh_ValidUVs.cpp │ │ │ │ ├── LintRule_Blueprint_Funcs_PublicDescriptions.cpp │ │ │ │ ├── LintRule_Blueprint_Vars_ConfigCategories.cpp │ │ │ │ ├── LintRule_Path_NoTopLevel.cpp │ │ │ │ ├── LintRule_Path_Regex.cpp │ │ │ │ ├── LintRule_ParticleSystem_EmitterNameRegex.cpp │ │ │ │ ├── LintRule_Texture_Size_PowerOfTwo.cpp │ │ │ │ ├── LintRule_Blueprint_Funcs_MaxNodes.cpp │ │ │ │ └── LintRule_IsNamedCorrectly_Base.cpp │ │ │ ├── UI │ │ │ │ ├── SAssetLinkWidget.cpp │ │ │ │ ├── LintReportAssetErrorList.cpp │ │ │ │ ├── LintReportRuleErrorList.cpp │ │ │ │ ├── LintReportRuleError.cpp │ │ │ │ ├── LintReportAssetError.cpp │ │ │ │ ├── LintReportAssetDetails.cpp │ │ │ │ ├── SStepWidget.cpp │ │ │ │ └── LintReportRuleDetails.cpp │ │ │ ├── LintRunner.cpp │ │ │ ├── LinterNamingConvention.cpp │ │ │ ├── Linter.cpp │ │ │ ├── LinterStyle.cpp │ │ │ ├── LintRuleSet.cpp │ │ │ └── TooltipTool │ │ │ │ └── TooltipStringHelper.cpp │ │ ├── Public │ │ │ ├── AnyObject_LinterDummyClass.h │ │ │ ├── LinterCommandlet.h │ │ │ ├── LinterStyle.h │ │ │ ├── UI │ │ │ │ ├── LintReportRuleError.h │ │ │ │ ├── LintReportAssetError.h │ │ │ │ ├── LintReportAssetErrorList.h │ │ │ │ ├── LintReportRuleErrorList.h │ │ │ │ ├── SAssetLinkWidget.h │ │ │ │ ├── LintReportAssetDetails.h │ │ │ │ ├── LintReportRuleDetails.h │ │ │ │ ├── LintReport.h │ │ │ │ ├── LintWizard.h │ │ │ │ └── SStepWidget.h │ │ │ ├── LinterSettings.h │ │ │ ├── LinterContentBrowserExtensions.h │ │ │ ├── LintRules │ │ │ │ ├── LintRule_Blueprint_LooseNodes.h │ │ │ │ ├── LintRule_Blueprint_Compiles.h │ │ │ │ ├── LintRule_Blueprint_Vars_NoConfigFlag.h │ │ │ │ ├── LintRule_Blueprint_Vars_PluralArrays.h │ │ │ │ ├── LintRule_Blueprint_Funcs_MustHaveReturn.h │ │ │ │ ├── LintRule_Blueprint_Base.h │ │ │ │ ├── LintRule_Blueprint_Funcs_PublicDescriptions.h │ │ │ │ ├── LintRule_Collection.h │ │ │ │ ├── LintRule_Path_IsNotTooLong.h │ │ │ │ ├── LintRule_Blueprint_Vars_EditableMustHaveTooltip.h │ │ │ │ ├── LintRule_Blueprint_Vars_NonAtomic.h │ │ │ │ ├── LintRule_Blueprint_Vars_ConfigCategories.h │ │ │ │ ├── LintRule_IsNamedCorrectly_Base.h │ │ │ │ ├── LintRule_Path_DisallowNames.h │ │ │ │ ├── LintRule_Blueprint_Funcs_MaxNodes.h │ │ │ │ ├── LintRule_Path_NoTopLevel.h │ │ │ │ ├── LintRule_StaticMesh_ValidUVs.h │ │ │ │ ├── LintRule_SoundWave_SampleRate.h │ │ │ │ ├── LintRule_Blueprint_Vars_Regex.h │ │ │ │ ├── LintRule_Texture_Size_PowerOfTwo.h │ │ │ │ ├── LintRule_Texture_Size_NotTooBig.h │ │ │ │ ├── LintRule_ParticleSystem_EmitterNameRegex.h │ │ │ │ └── LintRule_Path_Regex.h │ │ │ ├── LintRunner.h │ │ │ ├── TooltipEditor │ │ │ │ ├── TooltipStringHelper.h │ │ │ │ └── TooltipTool.h │ │ │ ├── Linter.h │ │ │ ├── LintRuleSet.h │ │ │ ├── LinterNamingConvention.h │ │ │ ├── BatchRenameTool │ │ │ │ └── BatchRenameTool.h │ │ │ ├── LinterBase.h │ │ │ └── LintRule.h │ │ └── Linter.Build.cs │ ├── GamemakinLinter │ │ ├── Public │ │ │ ├── GamemakinNamingConvention.h │ │ │ └── GamemakinLinter.h │ │ ├── Private │ │ │ ├── GamemakinLinter.cpp │ │ │ └── GamemakinNamingConvention.cpp │ │ └── GamemakinLinter.Build.cs │ └── MarketplaceLinter │ │ ├── Public │ │ ├── MarketplaceLinter.h │ │ └── MarketplaceNamingConvention.h │ │ ├── Private │ │ ├── MarketplaceLinter.cpp │ │ └── MarketplaceNamingConvention.cpp │ │ └── MarketplaceLinter.Build.cs │ └── Linter.uplugin ├── README.md └── LICENSE /docs/img/LintReport.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/LintReport.png -------------------------------------------------------------------------------- /docs/img/LintRulesMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/LintRulesMap.png -------------------------------------------------------------------------------- /docs/img/LintRulesInBP.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/LintRulesInBP.png -------------------------------------------------------------------------------- /docs/img/LinterLauncher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/LinterLauncher.png -------------------------------------------------------------------------------- /docs/img/ScanWithLinter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/ScanWithLinter.png -------------------------------------------------------------------------------- /docs/img/ShowPluginContent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/ShowPluginContent.png -------------------------------------------------------------------------------- /docs/img/MarketplaceLintRuleSet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/MarketplaceLintRuleSet.png -------------------------------------------------------------------------------- /Plugins/Linter/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Resources/Icon128.png -------------------------------------------------------------------------------- /docs/img/MarketplaceLinterFolder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/docs/img/MarketplaceLinterFolder.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_MapCheck_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_MapCheck_64x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Linter 2 | 3 | Currently WIP release of Linter. 4 | 5 | @TODO: 6 | 7 | * 4.27 patchups per community 8 | * Compilation and release guide -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_AddContent_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_AddContent_64x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_Help_epic_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_Help_epic_16x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_Texture_Run_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_Texture_Run_16x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_file_saveall_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_file_saveall_64x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_tab_Toolbars_64x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_tab_Toolbars_64x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/CompileStatus_Fail_64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/CompileStatus_Fail_64px.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/CompileStatus_Good_64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/CompileStatus_Good_64px.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_MessageLog_Info_14x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_MessageLog_Info_14x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/CompileStatus_Unknown_64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/CompileStatus_Unknown_64px.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/CompileStatus_Warning_64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/CompileStatus_Warning_64px.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/CompileStatus_Working_64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/CompileStatus_Working_64px.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_Help_Documentation_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_Help_Documentation_16x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_MessageLog_Error_14x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_MessageLog_Error_14x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/Icons/icon_MessageLog_Warning_14x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/Icons/icon_MessageLog_Warning_14x.png -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/GamemakinLinterRuleSet.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_Maps.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_Maps.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/MarketplaceLintRuleSet.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/MarketplaceLintRuleSet.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/GamemakinNamingConvention.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/GamemakinNamingConvention.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Compiles.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Compiles.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_PathChecks.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_PathChecks.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_PathRules.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_PathRules.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoIs.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoIs.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoSpaces.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoSpaces.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoUnicode.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoUnicode.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/MarketplaceNamingConvention.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/MarketplaceNamingConvention.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_NoConfigFlag.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_NoConfigFlag.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_IsNamedCorrectly.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_IsNamedCorrectly.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_PascalCase.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_PascalCase.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MaxNodes.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MaxNodes.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MustReturn.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_MustReturn.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NonAtomic.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NonAtomic.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_PascalCase.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_PascalCase.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_RedundantNames.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_RedundantNames.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_StaticMesh_ValidUVs.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_StaticMesh_ValidUVs.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_PascalCase.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_PascalCase.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_DoNotNameAssets.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_DoNotNameAssets.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoTopLevelAssets.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Path_NoTopLevelAssets.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_PowerOfTwo.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_PowerOfTwo.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_StaticMesh_ValidUVs.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_StaticMesh_ValidUVs.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_ConfigCategories.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_ConfigCategories.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_EditableTooltips.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_EditableTooltips.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoPluralArrays.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Vars_NoPluralArrays.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Particles_EmitterNames.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Particles_EmitterNames.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_Size_NotTooBig.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_Texture2D_Size_NotTooBig.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.uasset -------------------------------------------------------------------------------- /Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_PublicDescriptions.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ue4plugins/Linter/HEAD/Plugins/Linter/Content/GamemakinLinter/LintRules/GMLR_BP_Funcs_PublicDescriptions.uasset -------------------------------------------------------------------------------- /docs/style.md: -------------------------------------------------------------------------------- 1 | # Gamemakin LLC Style Guide 2 | 3 | For information about how Linter scans against the Gamemakin LLC Style Guide, please see the style guide itself at [http://ue4.style](http://ue4.style) as the style guide is marked up with its own Linter annotations. -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/AnyObject_LinterDummyClass.cpp: -------------------------------------------------------------------------------- 1 | #include "AnyObject_LinterDummyClass.h" 2 | 3 | UAnyObject_LinterDummyClass::UAnyObject_LinterDummyClass(const FObjectInitializer& ObjectInitializer) 4 | : Super(ObjectInitializer) 5 | { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/AnyObject_LinterDummyClass.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "AnyObject_LinterDummyClass.generated.h" 6 | 7 | UCLASS(BlueprintType, Blueprintable) 8 | class UAnyObject_LinterDummyClass : public UObject 9 | { 10 | GENERATED_BODY() 11 | 12 | public: 13 | 14 | UAnyObject_LinterDummyClass(const FObjectInitializer& ObjectInitializer); 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LinterCommandlet.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "Commandlets/Commandlet.h" 5 | #include "LinterCommandlet.generated.h" 6 | 7 | UCLASS() 8 | class ULinterCommandlet : public UCommandlet 9 | { 10 | GENERATED_UCLASS_BODY() 11 | //~ Begin UCommandlet Interface 12 | virtual int32 Main(const FString& Params) override; 13 | 14 | //~ End UCommandlet Interface 15 | }; 16 | 17 | 18 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/GamemakinLinter/Public/GamemakinNamingConvention.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LinterNamingConvention.h" 6 | #include "GamemakinNamingConvention.generated.h" 7 | 8 | UCLASS() 9 | class UGamemakinNamingConvention : public ULinterNamingConvention 10 | { 11 | GENERATED_BODY() 12 | 13 | public: 14 | 15 | UGamemakinNamingConvention(const FObjectInitializer& ObjectInitializer); 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceLinter.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | DECLARE_LOG_CATEGORY_EXTERN(LogMarketplaceLinter, Verbose, All); 8 | 9 | class MARKETPLACELINTER_API FMarketplaceLinterModule : public IModuleInterface 10 | { 11 | public: 12 | 13 | /** IModuleInterface implementation */ 14 | virtual void StartupModule() override; 15 | virtual void ShutdownModule() override; 16 | 17 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/GamemakinLinter/Private/GamemakinLinter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #include "GamemakinLinter.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FGamemakinLinterModule" 6 | 7 | void FGamemakinLinterModule::StartupModule() 8 | { 9 | // Super::StartupModule(); 10 | } 11 | 12 | void FGamemakinLinterModule::ShutdownModule() 13 | { 14 | // Super::ShutdownModule(); 15 | } 16 | 17 | #undef LOCTEXT_NAMESPACE 18 | 19 | IMPLEMENT_MODULE(FGamemakinLinterModule, GamemakinLinter) 20 | DEFINE_LOG_CATEGORY(LogGamemakinLinter); -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LinterStyle.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "CoreTypes.h" 5 | #include "Styling/ISlateStyle.h" 6 | 7 | class FLinterStyle 8 | { 9 | public: 10 | static void Initialize(); 11 | 12 | static void Shutdown(); 13 | 14 | static TSharedPtr< class ISlateStyle > Get(); 15 | static TSharedPtr< class FSlateStyleSet > StyleSet; 16 | 17 | static FName GetStyleSetName(); 18 | private: 19 | static FString InContent(const FString& RelativePath, const ANSICHAR* Extension); 20 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReportRuleError.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | #include "Widgets/SCompoundWidget.h" 4 | #include "LintRule.h" 5 | 6 | class SLintReportRuleError : public SCompoundWidget 7 | { 8 | public: 9 | 10 | SLATE_BEGIN_ARGS(SLintReportRuleError) 11 | { 12 | } 13 | SLATE_ATTRIBUTE(TSharedPtr, RuleViolation) 14 | 15 | SLATE_END_ARGS() 16 | 17 | TAttribute> RuleViolation; 18 | 19 | 20 | public: 21 | 22 | void Construct(const FArguments& Args); 23 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReportAssetError.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | #include "Widgets/SCompoundWidget.h" 4 | #include "LintRule.h" 5 | 6 | class SLintReportAssetError : public SCompoundWidget 7 | { 8 | public: 9 | 10 | SLATE_BEGIN_ARGS(SLintReportAssetError) 11 | { 12 | } 13 | SLATE_ATTRIBUTE(TSharedPtr, RuleViolation) 14 | 15 | SLATE_END_ARGS() 16 | 17 | TAttribute> RuleViolation; 18 | 19 | 20 | public: 21 | 22 | void Construct(const FArguments& Args); 23 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/MarketplaceLinter/Public/MarketplaceNamingConvention.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "UObject/Object.h" 6 | #include "Templates/SharedPointer.h" 7 | #include "LinterNamingConvention.h" 8 | #include "MarketplaceNamingConvention.generated.h" 9 | 10 | 11 | UCLASS() 12 | class UMarketplaceNamingConvention : public ULinterNamingConvention 13 | { 14 | GENERATED_BODY() 15 | 16 | public: 17 | 18 | UMarketplaceNamingConvention(const FObjectInitializer& ObjectInitializer); 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LinterSettings.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "CoreMinimal.h" 5 | #include "UObject/Object.h" 6 | #include "LintRuleSet.h" 7 | #include "LinterSettings.generated.h" 8 | 9 | 10 | /** 11 | * Implements the settings for the Linter plugin. 12 | */ 13 | UCLASS(config = Linter, defaultconfig) 14 | class ULinterSettings : public UObject 15 | { 16 | GENERATED_UCLASS_BODY() 17 | 18 | public: 19 | 20 | UPROPERTY(EditAnywhere, config, Category = Settings) 21 | TAssetPtr DefaultLintRuleSet; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/GamemakinLinter/Public/GamemakinLinter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "Modules/ModuleManager.h" 5 | 6 | DECLARE_LOG_CATEGORY_EXTERN(LogGamemakinLinter, Verbose, All); 7 | 8 | class GAMEMAKINLINTER_API FGamemakinLinterModule : public IModuleInterface 9 | { 10 | public: 11 | 12 | /** IModuleInterface implementation */ 13 | virtual void StartupModule() override; 14 | virtual void ShutdownModule() override; 15 | 16 | virtual bool SupportsDynamicReloading() override 17 | { 18 | return false; 19 | } 20 | 21 | private: 22 | 23 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LinterContentBrowserExtensions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | 6 | // Integrate Linter actions into the Content Browser 7 | class FLinterContentBrowserExtensions 8 | { 9 | public: 10 | static void InstallHooks(FLinterModule* LinterModule, class FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle); 11 | static void RemoveHooks(FLinterModule* LinterModule, class FDelegateHandle* pContentBrowserExtenderDelegateHandle, class FDelegateHandle* pAssetExtenderDelegateHandle); 12 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReportAssetErrorList.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | #include "Widgets/SCompoundWidget.h" 4 | #include "Widgets/DeclarativeSyntaxSupport.h" 5 | 6 | class SLintReportAssetErrorList : public SCompoundWidget 7 | { 8 | public: 9 | 10 | SLATE_BEGIN_ARGS(SLintReportAssetErrorList) 11 | { 12 | } 13 | SLATE_ATTRIBUTE(TArray>, RuleViolations) 14 | 15 | SLATE_END_ARGS() 16 | 17 | TAttribute>> RuleViolations; 18 | 19 | public: 20 | 21 | void Construct(const FArguments& Args); 22 | 23 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReportRuleErrorList.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | #include "Widgets/SCompoundWidget.h" 4 | #include "Widgets/DeclarativeSyntaxSupport.h" 5 | 6 | class SLintReportRuleErrorList : public SCompoundWidget 7 | { 8 | public: 9 | 10 | SLATE_BEGIN_ARGS(SLintReportRuleErrorList) 11 | { 12 | } 13 | SLATE_ATTRIBUTE(TArray>, RuleViolations) 14 | 15 | SLATE_END_ARGS() 16 | 17 | TAttribute>> RuleViolations; 18 | 19 | public: 20 | 21 | void Construct(const FArguments& Args); 22 | 23 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LinterSettings.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "LinterSettings.h" 4 | #include "UObject/ConstructorHelpers.h" 5 | #include "LintRuleSet.h" 6 | 7 | 8 | ULinterSettings::ULinterSettings(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | if (DefaultLintRuleSet.IsNull()) 12 | { 13 | static ConstructorHelpers::FObjectFinder DefaultMarketplaceRuleSetRef(TEXT("LintRuleSet'/Linter/MarketplaceLinter/MarketplaceLintRuleSet.MarketplaceLintRuleSet'")); 14 | DefaultLintRuleSet = DefaultMarketplaceRuleSetRef.Object; 15 | } 16 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/GamemakinLinter/GamemakinLinter.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class GamemakinLinter : ModuleRules 6 | { 7 | public GamemakinLinter(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange( 12 | new string[] 13 | { 14 | "Core", 15 | "CoreUObject", 16 | "Engine", 17 | "Linter" 18 | } 19 | ); 20 | 21 | 22 | PrivateDependencyModuleNames.AddRange( 23 | new string[] 24 | { 25 | 26 | } 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/SAssetLinkWidget.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "EditorStyleSet.h" 5 | #include "LinterStyle.h" 6 | #include "Widgets/DeclarativeSyntaxSupport.h" 7 | #include "Widgets/SCompoundWidget.h" 8 | #include "Types/SlateStructs.h" 9 | #include "Misc/ScopedSlowTask.h" 10 | 11 | class SAssetLinkWidget : public SCompoundWidget 12 | { 13 | public: 14 | 15 | SLATE_BEGIN_ARGS(SAssetLinkWidget) 16 | { 17 | } 18 | 19 | SLATE_ATTRIBUTE(FAssetData, AssetData) 20 | 21 | SLATE_END_ARGS() 22 | 23 | TAttribute AssetData; 24 | 25 | public: 26 | 27 | void Construct(const FArguments& Args); 28 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceLinter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "MarketplaceLinter.h" 4 | #include "ISettingsModule.h" 5 | #include "Framework/Docking/TabManager.h" 6 | #include "LevelEditor.h" 7 | 8 | #include "Widgets/Input/SButton.h" 9 | #include "Styling/SlateStyle.h" 10 | 11 | #define LOCTEXT_NAMESPACE "FMarketplaceLinterModule" 12 | 13 | void FMarketplaceLinterModule::StartupModule() 14 | { 15 | 16 | } 17 | 18 | void FMarketplaceLinterModule::ShutdownModule() 19 | { 20 | 21 | } 22 | 23 | 24 | #undef LOCTEXT_NAMESPACE 25 | 26 | IMPLEMENT_MODULE(FMarketplaceLinterModule, MarketplaceLinter) 27 | DEFINE_LOG_CATEGORY(LogMarketplaceLinter); -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_LooseNodes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_LooseNodes.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_LooseNodes : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_LooseNodes(const FObjectInitializer& ObjectInitializer); 16 | 17 | protected: 18 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Compiles.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Compiles.generated.h" 8 | 9 | /** 10 | *Comment 11 | */ 12 | UCLASS(BlueprintType, Blueprintable, Abstract) 13 | class LINTER_API ULintRule_Blueprint_Compiles : public ULintRule_Blueprint_Base 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | ULintRule_Blueprint_Compiles(const FObjectInitializer& ObjectInitializer); 19 | 20 | protected: 21 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Vars_NoConfigFlag.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Vars_NoConfigFlag : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Vars_NoConfigFlag(const FObjectInitializer& ObjectInitializer); 16 | 17 | protected: 18 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_PluralArrays.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Vars_PluralArrays.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Vars_PluralArrays : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Vars_PluralArrays(const FObjectInitializer& ObjectInitializer); 16 | 17 | protected: 18 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Funcs_MustHaveReturn.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Funcs_MustHaveReturn : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Funcs_MustHaveReturn(const FObjectInitializer& ObjectInitializer); 16 | 17 | protected: 18 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Base.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Base.generated.h" 8 | 9 | /** 10 | *Comment 11 | */ 12 | UCLASS(BlueprintType, Blueprintable, Abstract) 13 | class LINTER_API ULintRule_Blueprint_Base : public ULintRule 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | ULintRule_Blueprint_Base(const FObjectInitializer& ObjectInitializer); 19 | 20 | // This does rule pre-checks. You probably want to override PassesRule_Internal_Implementation 21 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | }; 23 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Funcs_PublicDescriptions.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Funcs_PublicDescriptions : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Funcs_PublicDescriptions(const FObjectInitializer& ObjectInitializer); 16 | 17 | protected: 18 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Collection.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Collection.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Collection : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Collection(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | TArray> SubRules; 19 | 20 | protected: 21 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_IsNotTooLong.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Path_IsNotTooLong.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Path_IsNotTooLong : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Path_IsNotTooLong(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | int32 MaxPathLimit = 140; 19 | 20 | protected: 21 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Vars_EditableMustHaveTooltip.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Vars_EditableMustHaveTooltip : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Vars_EditableMustHaveTooltip(const FObjectInitializer& ObjectInitializer); 16 | 17 | protected: 18 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReportAssetDetails.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | #include "Widgets/SCompoundWidget.h" 4 | #include "LintRule.h" 5 | 6 | class SLintReportAssetDetails : public SCompoundWidget 7 | { 8 | public: 9 | 10 | SLATE_BEGIN_ARGS(SLintReportAssetDetails) 11 | { 12 | } 13 | SLATE_ATTRIBUTE(FAssetData, AssetData) 14 | SLATE_ATTRIBUTE(TArray>, RuleViolations) 15 | SLATE_ATTRIBUTE(TSharedPtr, ThumbnailPool) 16 | 17 | SLATE_END_ARGS() 18 | 19 | TAttribute AssetData; 20 | TAttribute>> RuleViolations; 21 | TAttribute> ThumbnailPool; 22 | 23 | 24 | public: 25 | 26 | void Construct(const FArguments& Args); 27 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Base.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Base.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | 6 | ULintRule_Blueprint_Base::ULintRule_Blueprint_Base(const FObjectInitializer& ObjectInitializer) 7 | : Super(ObjectInitializer) 8 | { 9 | } 10 | 11 | bool ULintRule_Blueprint_Base::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 12 | { 13 | // If we aren't a blueprint, abort 14 | if (Cast(ObjectToLint) == nullptr) 15 | { 16 | // @TODO: Bubble up some sort of configuration error? 17 | return true; 18 | } 19 | 20 | return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); 21 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_NonAtomic.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Vars_NonAtomic.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Vars_NonAtomic : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Vars_NonAtomic(const FObjectInitializer& ObjectInitializer); 16 | 17 | static bool IsVariableAtomic(FBPVariableDescription& VarDesc); 18 | 19 | protected: 20 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 21 | 22 | }; 23 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReportRuleDetails.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | #include "Widgets/SCompoundWidget.h" 4 | #include "LintRule.h" 5 | 6 | class SLintReportRuleDetails : public SCompoundWidget 7 | { 8 | public: 9 | 10 | SLATE_BEGIN_ARGS(SLintReportRuleDetails) 11 | { 12 | } 13 | SLATE_ATTRIBUTE(TArray>, RuleViolations) 14 | SLATE_ATTRIBUTE(TSharedPtr, ThumbnailPool) 15 | 16 | SLATE_END_ARGS() 17 | 18 | TAttribute>> RuleViolations; 19 | TAttribute> ThumbnailPool; 20 | 21 | 22 | public: 23 | 24 | void Construct(const FArguments& Args); 25 | 26 | private: 27 | FString RuleURL; 28 | FAssetData RuleAssetData; 29 | TSharedPtr ThumbnailBox; 30 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_ConfigCategories.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Vars_ConfigCategories.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Vars_ConfigCategories : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 15 | int32 NumVariablesToRequireCategorization = 5; 16 | 17 | public: 18 | ULintRule_Blueprint_Vars_ConfigCategories(const FObjectInitializer& ObjectInitializer); 19 | 20 | protected: 21 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_IsNamedCorrectly_Base.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | #include "LintRule_IsNamedCorrectly_Base.generated.h" 7 | 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_IsNamedCorrectly_Base : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_IsNamedCorrectly_Base(const FObjectInitializer& ObjectInitializer); 16 | 17 | UFUNCTION(BlueprintCallable, Category="Lint") 18 | static FString BuildSuggestedName(FString CurrentName, FString DesiredPrefix, FString DesiredSuffix = TEXT("")); 19 | 20 | protected: 21 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_DisallowNames.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Path_DisallowNames.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Path_DisallowNames : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Path_DisallowNames(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | TArray DisallowedFolderNames; 19 | 20 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 21 | FText RecommendedAction; 22 | 23 | protected: 24 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Funcs_MaxNodes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Funcs_MaxNodes.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Funcs_MaxNodes : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Funcs_MaxNodes(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | int32 MaxExpectedNonTrivialNodes = 50; 19 | 20 | static bool IsNodeTrivial(const UEdGraphNode* Node); 21 | 22 | protected: 23 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_NoTopLevel.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Path_NoTopLevel.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Path_NoTopLevel : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Path_NoTopLevel(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Display") 18 | FText ZeroTopLevelFoldersRecommendedAction; 19 | 20 | UPROPERTY(EditDefaultsOnly, Category = "Display") 21 | FText PleaseUseThisFolderRecommendedAction; 22 | 23 | protected: 24 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_StaticMesh_ValidUVs.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_StaticMesh_ValidUVs.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_StaticMesh_ValidUVs : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_StaticMesh_ValidUVs(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | bool bIgnoreMissingUVs = false; 19 | 20 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 21 | 22 | protected: 23 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_SoundWave_SampleRate.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_SoundWave_SampleRate.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_SoundWave_SampleRate : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_SoundWave_SampleRate(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | TArray ValidSampleRates; 19 | 20 | 21 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | protected: 24 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRunner.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "HAL/ThreadSafeCounter.h" 6 | #include "HAL/Runnable.h" 7 | #include "AssetData.h" 8 | #include "Linter.h" 9 | 10 | class FLintRunner : public FRunnable 11 | { 12 | 13 | public: 14 | 15 | FLintRunner(UObject* InLoadedObject, const ULintRuleSet* LintRuleSet, TArray* InpOutRuleViolations, FScopedSlowTask* InParentScopedSlowTask); 16 | 17 | virtual bool RequiresGamethread(); 18 | 19 | virtual bool Init() override; 20 | virtual uint32 Run() override; 21 | virtual void Stop() override; 22 | virtual void Exit() override; 23 | 24 | protected: 25 | UObject* LoadedObject = nullptr; 26 | const ULintRuleSet* RuleSet = nullptr; 27 | TArray* pOutRuleViolations; 28 | 29 | const FLintRuleList* pLoadedRuleList; 30 | static FCriticalSection LintDataUpdateLock; 31 | 32 | FScopedSlowTask* ParentScopedSlowTask; 33 | }; 34 | 35 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Blueprint_Vars_Regex.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Blueprint_Vars_Regex.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Blueprint_Vars_Regex : public ULintRule_Blueprint_Base 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Blueprint_Vars_Regex(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | bool bUseLowercaseBPrefixForBooleans = true; 19 | 20 | UPROPERTY(EditAnywhere, Category = "Settings") 21 | FString RegexPatternString; 22 | 23 | UPROPERTY(EditAnywhere, Category = "Settings") 24 | bool bMustNotContainRegexPattern = true; 25 | 26 | protected: 27 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 28 | 29 | }; 30 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_PowerOfTwo.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | #include "Engine/TextureDefines.h" 7 | 8 | #include "LintRule_Texture_Size_PowerOfTwo.generated.h" 9 | 10 | UCLASS(BlueprintType, Blueprintable, Abstract) 11 | class LINTER_API ULintRule_Texture_Size_PowerOfTwo : public ULintRule 12 | { 13 | GENERATED_BODY() 14 | 15 | public: 16 | ULintRule_Texture_Size_PowerOfTwo(const FObjectInitializer& ObjectInitializer); 17 | 18 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 19 | TSet> IgnoreTexturesInTheseGroups; 20 | 21 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 22 | 23 | protected: 24 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Texture_Size_NotTooBig.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Texture_Size_NotTooBig.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Texture_Size_NotTooBig : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Texture_Size_NotTooBig(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 18 | int32 MaxTextureSizeX = 8192; 19 | 20 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 21 | int32 MaxTextureSizeY = 8192; 22 | 23 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 24 | 25 | protected: 26 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 27 | 28 | }; 29 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_IsNotTooLong.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Path_IsNotTooLong.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_Path_IsNotTooLong::ULintRule_Path_IsNotTooLong(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Path_IsNotTooLong::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | FString PathName = ObjectToLint->GetPathName(); 15 | 16 | // See if file path is longer than 140 characters 17 | // 145 = 140 + /Game (5) 18 | int32 DotIndex = -1; 19 | PathName.FindLastChar('.', DotIndex); 20 | FString FilePath = PathName.LeftChop(PathName.Len() - DotIndex); 21 | if (FilePath.Len() >= MaxPathLimit + 5) 22 | { 23 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); 24 | return false; 25 | } 26 | 27 | return true; 28 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintReport.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "Widgets/SCompoundWidget.h" 5 | #include "Widgets/Layout/SScrollBox.h" 6 | 7 | #include "LintReportAssetError.h" 8 | #include "LintRule.h" 9 | 10 | 11 | class SLintReport : public SCompoundWidget 12 | { 13 | public: 14 | 15 | SLATE_BEGIN_ARGS(SLintReport) 16 | { 17 | } 18 | 19 | SLATE_END_ARGS() 20 | 21 | public: 22 | 23 | void Construct(const FArguments& Args); 24 | void Rebuild(const ULintRuleSet* SelectedLintRuleSet); 25 | TSharedRef GetViewButtonContent(); 26 | 27 | const ULintRuleSet* LastUsedRuleSet = nullptr; 28 | 29 | TSharedPtr ResultsTextBlockPtr; 30 | TArray> RuleViolations; 31 | TSharedPtr ViewOptionsComboButton; 32 | TSharedPtr AssetDetailsScrollBoxPtr; 33 | TSharedPtr RuleDetailsScrollBoxPtr; 34 | FString JsonReport; 35 | FString HTMLReport; 36 | 37 | bool bHasRanReport = false; 38 | int32 NumErrors = 0; 39 | int32 NumWarnings = 0; 40 | 41 | 42 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Unreal Engine Plug-ins 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Compiles.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Compiles.h" 3 | #include "LintRuleSet.h" 4 | #include "Sound/SoundWave.h" 5 | #include "Engine/Blueprint.h" 6 | 7 | ULintRule_Blueprint_Compiles::ULintRule_Blueprint_Compiles(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Blueprint_Compiles::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 15 | 16 | switch (Blueprint->Status) 17 | { 18 | case EBlueprintStatus::BS_BeingCreated: 19 | case EBlueprintStatus::BS_Dirty: 20 | case EBlueprintStatus::BS_Unknown: 21 | case EBlueprintStatus::BS_UpToDate: 22 | return true; 23 | case EBlueprintStatus::BS_Error: 24 | case EBlueprintStatus::BS_UpToDateWithWarnings: 25 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); 26 | return false; 27 | default: 28 | return true; 29 | } 30 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Collection.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Collection.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_Collection::ULintRule_Collection(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Collection::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | bool bRuleViolated = false; 15 | 16 | for (TSubclassOf LintRuleClass : SubRules) 17 | { 18 | if (LintRuleClass.Get() != nullptr) 19 | { 20 | const ULintRule* LintRule = GetDefault(LintRuleClass); 21 | if (LintRule != nullptr) 22 | { 23 | TArray SubViolations; 24 | if (!LintRule->PassesRule(ObjectToLint, ParentRuleSet, SubViolations)) 25 | { 26 | OutRuleViolations.Append(SubViolations); 27 | bRuleViolated = true; 28 | } 29 | } 30 | } 31 | } 32 | 33 | return !bRuleViolated; 34 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_DisallowNames.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Path_DisallowNames.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_Path_DisallowNames::ULintRule_Path_DisallowNames(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | RecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_DisallowNames_ChangeName", "Please rename \"{0}\" to an allowed name."); 11 | } 12 | 13 | bool ULintRule_Path_DisallowNames::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 14 | { 15 | FString PathName = ObjectToLint->GetPathName(); 16 | TArray PathElements; 17 | PathName.ParseIntoArray(PathElements, TEXT("/"), true); 18 | 19 | bool bRuleViolated = false; 20 | 21 | for (int32 i = 0; i < PathElements.Num() - 1; ++i) 22 | { 23 | if (DisallowedFolderNames.Contains(PathElements[i])) 24 | { 25 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, FText::FromString(PathElements[i])))); 26 | bRuleViolated = true; 27 | } 28 | } 29 | 30 | return !bRuleViolated; 31 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Linter.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class Linter : ModuleRules 6 | { 7 | public Linter(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange( 12 | new string[] 13 | { 14 | "Core", 15 | } 16 | ); 17 | 18 | 19 | PrivateDependencyModuleNames.AddRange( 20 | new string[] 21 | { 22 | "CoreUObject", 23 | "Engine", 24 | "Slate", 25 | "SlateCore", 26 | "AppFramework", 27 | "InputCore", 28 | "UnrealEd", 29 | "GraphEditor", 30 | "AssetTools", 31 | "EditorStyle", 32 | "BlueprintGraph", 33 | "PropertyEditor", 34 | "LauncherPlatform", 35 | "Projects", 36 | "DesktopPlatform", 37 | "Json", 38 | "UATHelper" 39 | // ... add private dependencies that you statically link with here ... 40 | } 41 | ); 42 | 43 | PublicIncludePathModuleNames.Add("Launch"); 44 | 45 | #if UE_4_20_OR_LATER 46 | PublicDefinitions.Add("UE_4_20_OR_LATER=1"); 47 | #endif 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /docs/gettingstarted.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ## Requirements 4 | 5 | * You will need a launcher version of Unreal Engine 4 version 4.24 or later. 6 | * You must purchase (for free) the Linter plugin on the Unreal Engine Marketplace. 7 | 8 | @TODO: Add link here once I have it 9 | 10 | ## Installing From The Launcher 11 | 12 | 1. Close all your instances of Unreal Engine 4 13 | 2. Using the Epic Launcher find Linter in your Vault 14 | 3. Use the Install to Engine button to install Linter to your Engine 15 | 16 | ![](img/LinterLauncher.png) 17 | 18 | ## Enabling Linter 19 | 20 | 1. Open your project 21 | 2. Open the Plugins window by clicking Edit on the main toolbar and navigating to Plugins 22 | 3. Search for Linter 23 | 4. Enable the Linter plugin by ensuring the Enabled checkbox is checked 24 | 5. Restart the editor 25 | 26 | ## Using Linter 27 | 28 | Once installed there isn't much in the way of using Linter. Operating Linter is pretty straightforward: 29 | 30 | 1. Right-click on a content folder in your project 31 | 2. Click "Scan with Linter" 32 | 3. Select the ruleset you would like to use for linting 33 | 4. Wait for the results 34 | 35 | ![](img/ScanWithLinter.png) 36 | 37 | ## The Lint Report 38 | 39 | Once a project is scanned, you will be presented with a Lint Report that provides an overall summary of the state of your project. 40 | 41 | ![](img/LintReport.png) -------------------------------------------------------------------------------- /Plugins/Linter/Source/MarketplaceLinter/MarketplaceLinter.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class MarketplaceLinter : ModuleRules 6 | { 7 | public MarketplaceLinter(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange( 12 | new string[] 13 | { 14 | "Core", 15 | "Linter" 16 | } 17 | ); 18 | 19 | 20 | PrivateDependencyModuleNames.AddRange( 21 | new string[] 22 | { 23 | "CoreUObject", 24 | "Engine", 25 | "Slate", 26 | "SlateCore", 27 | "RenderCore", 28 | "UnrealEd", 29 | "GraphEditor", 30 | "AssetTools", 31 | "EditorStyle", 32 | "Projects", 33 | "BlueprintGraph", 34 | "InputCore", 35 | "StandaloneRenderer", 36 | "PropertyEditor", 37 | "LevelEditor", 38 | "LauncherPlatform", 39 | "AppFramework", 40 | "DesktopPlatform", 41 | "UATHelper" 42 | // ... add private dependencies that you statically link with here ... 43 | } 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_ParticleSystem_EmitterNameRegex.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_ParticleSystem_EmitterNameRegex.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_ParticleSystem_EmitterNameRegex : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_ParticleSystem_EmitterNameRegex(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditAnywhere, Category = "Settings") 18 | int32 MinEmittersNeededToEnforce = 2; 19 | 20 | UPROPERTY(EditAnywhere, Category="Settings") 21 | FString RegexPatternString; 22 | 23 | UPROPERTY(EditAnywhere, Category = "Settings") 24 | bool bMustNotContainRegexPattern = true; 25 | 26 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 27 | FText DisallowedRecommendedAction; 28 | 29 | UPROPERTY(EditDefaultsOnly, Category = "Settings") 30 | FText NonConformingRecommendedAction; 31 | 32 | 33 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 34 | 35 | protected: 36 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 37 | 38 | }; 39 | -------------------------------------------------------------------------------- /Plugins/Linter/Linter.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 10, 4 | "VersionName": "2.3", 5 | "FriendlyName": "Linter", 6 | "Description": "", 7 | "Category": "Other", 8 | "CreatedBy": "Michael Allar", 9 | "CreatedByURL": "https://gamemak.in", 10 | "DocsURL": "http://discord.gamemak.in", 11 | "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/ca0639af6339476da86fa3bcf15de8ec", 12 | "SupportURL": "http://discord.gamemak.in", 13 | "EngineVersion": "4.26.0", 14 | "CanContainContent": true, 15 | "Installed": true, 16 | "Modules": [ 17 | { 18 | "Name": "Linter", 19 | "Type": "Editor", 20 | "LoadingPhase": "PreDefault", 21 | "WhitelistPlatforms": [ 22 | "Win64", 23 | "Win32", 24 | "Mac", 25 | "Linux" 26 | ], 27 | "BlacklistTargets": [ 28 | "Server", 29 | "Client" 30 | ] 31 | }, 32 | { 33 | "Name": "GamemakinLinter", 34 | "Type": "Editor", 35 | "LoadingPhase": "PreDefault", 36 | "WhitelistPlatforms": [ 37 | "Win64", 38 | "Win32", 39 | "Mac", 40 | "Linux" 41 | ], 42 | "BlacklistTargets": [ 43 | "Server", 44 | "Client" 45 | ] 46 | }, 47 | { 48 | "Name": "MarketplaceLinter", 49 | "Type": "Editor", 50 | "LoadingPhase": "PreDefault", 51 | "WhitelistPlatforms": [ 52 | "Win64", 53 | "Win32", 54 | "Mac", 55 | "Linux" 56 | ], 57 | "BlacklistTargets": [ 58 | "Server", 59 | "Client" 60 | ] 61 | } 62 | ] 63 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRules/LintRule_Path_Regex.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.h" 6 | 7 | #include "LintRule_Path_Regex.generated.h" 8 | 9 | UCLASS(BlueprintType, Blueprintable, Abstract) 10 | class LINTER_API ULintRule_Path_Regex : public ULintRule 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | ULintRule_Path_Regex(const FObjectInitializer& ObjectInitializer); 16 | 17 | UPROPERTY(EditAnywhere, Category="Settings") 18 | FString RegexPatternString; 19 | 20 | UPROPERTY(EditAnywhere, Category = "Settings") 21 | bool bMustNotContainRegexPattern = true; 22 | 23 | UPROPERTY(EditAnywhere, Category = "Settings") 24 | bool bCheckPerPathElement = true; 25 | 26 | UPROPERTY(EditDefaultsOnly, Category = "Settings|Whole Path") 27 | FText DisallowedWholePathRecommendedAction; 28 | 29 | UPROPERTY(EditDefaultsOnly, Category = "Settings|Whole Path") 30 | FText NonConformingWholePathRecommendedAction; 31 | 32 | UPROPERTY(EditDefaultsOnly, Category = "Settings|Path Element") 33 | FText DisallowedPathElementRecommendedAction; 34 | 35 | UPROPERTY(EditDefaultsOnly, Category = "Settings|Path Element") 36 | FText NonConformingPathElementRecommendedAction; 37 | 38 | protected: 39 | virtual bool PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const override; 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/SAssetLinkWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | #include "UI/SAssetLinkWidget.h" 3 | #include "Widgets/SBoxPanel.h" 4 | #include "Widgets/Layout/SBorder.h" 5 | #include "Widgets/Layout/SBox.h" 6 | #include "Widgets/Images/SImage.h" 7 | #include "Widgets/Input/SButton.h" 8 | #include "Widgets/Text/STextBlock.h" 9 | #include "Widgets/Images/SThrobber.h" 10 | #include "Widgets/Text/SRichTextBlock.h" 11 | 12 | BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 13 | void SAssetLinkWidget::Construct(const FArguments& Args) 14 | { 15 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 16 | AssetData = Args._AssetData; 17 | 18 | ChildSlot 19 | [ 20 | SNew(SHorizontalBox) 21 | + SHorizontalBox::Slot() 22 | .AutoWidth() 23 | [ 24 | SNew(SHyperlink) 25 | .Text(FText::FromName(AssetData.Get().AssetName)) 26 | .OnNavigate_Lambda([&]() 27 | { 28 | FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); 29 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); 30 | TArray AssetDatas; 31 | AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(AssetData.Get().ObjectPath)); 32 | ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); 33 | }) 34 | ] 35 | ]; 36 | } 37 | END_SLATE_FUNCTION_BUILD_OPTIMIZATION 38 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipStringHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Internationalization/Regex.h" 4 | #include "CoreMinimal.h" 5 | #include "UObject/Class.h" 6 | #include "UObject/Object.h" 7 | 8 | /** 9 | * Helper struct for showing function tooltip widgets 10 | **/ 11 | struct FBPFunctionArgumentDescription 12 | { 13 | FText ArgumentName; 14 | FText Tooltip; 15 | FText ArgumentType; 16 | 17 | FBPFunctionArgumentDescription() 18 | { 19 | } 20 | 21 | FBPFunctionArgumentDescription(FText InArgumentName, FText InTooltip, FText InArgumentType = FText::GetEmpty()) 22 | : ArgumentName(InArgumentName) 23 | , Tooltip(InTooltip) 24 | , ArgumentType(InArgumentType) 25 | { 26 | } 27 | 28 | FText GetToolTipTextRef() 29 | { 30 | return Tooltip; 31 | } 32 | }; 33 | 34 | class FTooltipStringHelper 35 | { 36 | public: 37 | static FText ParseFunctionRawTooltipGetDescription(FString RawTooltip, bool bRemoveNewlines = false); 38 | static bool ParseFunctionRawTooltip(FString RawTooltip, FText& OutFunctionDescription, TArray>& Inputs, TArray>& Outputs, FText& OutReturnText); 39 | static FString ConvertTooltipDataToRawTooltip(FText FunctionDescription, TArray> Inputs, TArray> Outputs); 40 | static bool FindAndUpdateArgumentTooltip(FText ArgumentName, FText Tooltip, TArray>& Arguments); 41 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/LintReportAssetErrorList.cpp: -------------------------------------------------------------------------------- 1 | #include "UI/LintReportAssetErrorList.h" 2 | #include "LinterStyle.h" 3 | #include "Widgets/Layout/SBorder.h" 4 | #include "EditorStyleSet.h" 5 | #include "Widgets/SBoxPanel.h" 6 | #include "Widgets/Layout/SExpandableArea.h" 7 | #include "ContentBrowserModule.h" 8 | #include "IContentBrowserSingleton.h" 9 | #include "AssetRegistryModule.h" 10 | #include "Widgets/Input/SHyperlink.h" 11 | #include "Widgets/Layout/SSpacer.h" 12 | #include "IAssetTools.h" 13 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 14 | #include "AssetToolsModule.h" 15 | #include "Misc/MessageDialog.h" 16 | #include "Internationalization/Internationalization.h" 17 | #include "Widgets/Text/STextBlock.h" 18 | #include "Framework/Views/ITypedTableView.h" 19 | #include "UI/LintReportAssetError.h" 20 | #include "LintRule.h" 21 | 22 | #define LOCTEXT_NAMESPACE "LintReport" 23 | 24 | void SLintReportAssetErrorList::Construct(const FArguments& Args) 25 | { 26 | RuleViolations = Args._RuleViolations; 27 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 28 | 29 | ChildSlot 30 | [ 31 | SNew(SListView>) 32 | .SelectionMode(ESelectionMode::None) 33 | .ListItemsSource(&RuleViolations.Get()) 34 | .OnGenerateRow_Lambda([&](TSharedPtr InItem, const TSharedRef& OwnerTable) 35 | { 36 | return SNew(STableRow>, OwnerTable) 37 | [ 38 | SNew(SLintReportAssetError) 39 | .RuleViolation(InItem) 40 | ]; 41 | }) 42 | ]; 43 | } 44 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/LintReportRuleErrorList.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #include "UI/LintReportruleErrorList.h" 4 | #include "LinterStyle.h" 5 | #include "Widgets/Layout/SBorder.h" 6 | #include "EditorStyleSet.h" 7 | #include "Widgets/SBoxPanel.h" 8 | #include "Widgets/Layout/SExpandableArea.h" 9 | #include "ContentBrowserModule.h" 10 | #include "IContentBrowserSingleton.h" 11 | #include "AssetRegistryModule.h" 12 | #include "Widgets/Input/SHyperlink.h" 13 | #include "Widgets/Layout/SSpacer.h" 14 | #include "IAssetTools.h" 15 | #include "AssetToolsModule.h" 16 | #include "Misc/MessageDialog.h" 17 | #include "Internationalization/Internationalization.h" 18 | #include "Widgets/Text/STextBlock.h" 19 | #include "Framework/Views/ITypedTableView.h" 20 | #include "UI/LintReportAssetError.h" 21 | #include "LintRule.h" 22 | 23 | #define LOCTEXT_NAMESPACE "LintReport" 24 | 25 | void SLintReportRuleErrorList::Construct(const FArguments& Args) 26 | { 27 | RuleViolations = Args._RuleViolations; 28 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 29 | 30 | ChildSlot 31 | [ 32 | SNew(SListView>) 33 | .SelectionMode(ESelectionMode::None) 34 | .ListItemsSource(&RuleViolations.Get()) 35 | .OnGenerateRow_Lambda([&](TSharedPtr InItem, const TSharedRef& OwnerTable) 36 | { 37 | return SNew(STableRow>, OwnerTable) 38 | [ 39 | SNew(SLintReportRuleError) 40 | .RuleViolation(InItem) 41 | ]; 42 | }) 43 | ]; 44 | } 45 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/Linter.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | #include "Widgets/Docking/SDockTab.h" 8 | #include "Styling/SlateStyle.h" 9 | 10 | class FLinterManagerBase; 11 | 12 | DECLARE_LOG_CATEGORY_EXTERN(LogLinter, Verbose, All); 13 | DECLARE_LOG_CATEGORY_EXTERN(LogCommandlet, All, All); 14 | 15 | class LINTER_API FLinterModule : public IModuleInterface 16 | { 17 | public: 18 | 19 | /** IModuleInterface implementation */ 20 | virtual void StartupModule() override; 21 | virtual void ShutdownModule() override; 22 | 23 | static TSharedRef SpawnTab(const FSpawnTabArgs& TabSpawnArgs, TSharedPtr StyleSet); 24 | 25 | virtual bool SupportsDynamicReloading() override 26 | { 27 | return false; 28 | } 29 | 30 | virtual TArray GetDesiredLintPaths() 31 | { 32 | if (DesiredLintPaths.Num() == 0) 33 | { 34 | DesiredLintPaths.Push(TEXT("/Game")); 35 | } 36 | 37 | return DesiredLintPaths; 38 | } 39 | virtual void SetDesiredLintPaths(TArray LintPaths) 40 | { 41 | DesiredLintPaths = LintPaths; 42 | if (DesiredLintPaths.Num() == 0) 43 | { 44 | DesiredLintPaths.Push(TEXT("/Game")); 45 | } 46 | } 47 | 48 | private: 49 | FDelegateHandle LevelEditorTabManagerChangedHandle; 50 | FDelegateHandle ContentBrowserExtenderDelegateHandle; 51 | FDelegateHandle AssetExtenderDelegateHandle; 52 | 53 | TArray DesiredLintPaths; 54 | public: 55 | void OnInitialAssetRegistrySearchComplete(); 56 | static void TryToLoadAllLintRuleSets(); 57 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NoConfigFlag.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Vars_NoConfigFlag.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | 7 | ULintRule_Blueprint_Vars_NoConfigFlag::ULintRule_Blueprint_Vars_NoConfigFlag(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Blueprint_Vars_NoConfigFlag::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 15 | 16 | bool bRuleViolated = false; 17 | 18 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsNoConfigFlag", "{Previous}{WhiteSpace}Please disable the config flag on variable {VarName}."); 19 | FText AllFixes; 20 | 21 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 22 | { 23 | FString PropName = Desc.VarName.ToString(); 24 | FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); 25 | 26 | if ((Desc.PropertyFlags & CPF_Config) == CPF_Config) 27 | { 28 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 29 | bRuleViolated = true; 30 | } 31 | } 32 | 33 | if (bRuleViolated) 34 | { 35 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 36 | return false; 37 | } 38 | 39 | return true; 40 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_PluralArrays.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Vars_PluralArrays.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | 7 | ULintRule_Blueprint_Vars_PluralArrays::ULintRule_Blueprint_Vars_PluralArrays(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Blueprint_Vars_PluralArrays::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 15 | 16 | bool bRuleViolated = false; 17 | 18 | FText FixTextTemplate = NSLOCTEXT("Linter", "PluralArrayHasArray", "{Previous}{WhiteSpace}Please remove the word 'Array' from your variable {VarName}."); 19 | FText AllFixes; 20 | 21 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 22 | { 23 | FString PropName = Desc.VarName.ToString(); 24 | FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); 25 | 26 | if (PropName.Contains(TEXT("Array"), ESearchCase::CaseSensitive)) 27 | { 28 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 29 | bRuleViolated = true; 30 | } 31 | } 32 | 33 | if (bRuleViolated) 34 | { 35 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 36 | return false; 37 | } 38 | 39 | return true; 40 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_SoundWave_SampleRate.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_SoundWave_SampleRate.h" 3 | #include "LintRuleSet.h" 4 | #include "Sound/SoundWave.h" 5 | 6 | ULintRule_SoundWave_SampleRate::ULintRule_SoundWave_SampleRate(const FObjectInitializer& ObjectInitializer) 7 | : Super(ObjectInitializer) 8 | { 9 | ValidSampleRates.Push(22050.0f); 10 | ValidSampleRates.Push(44100.0f); 11 | } 12 | 13 | bool ULintRule_SoundWave_SampleRate::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 14 | { 15 | // If we aren't a sound wave, abort 16 | if (Cast(ObjectToLint) == nullptr) 17 | { 18 | // @TODO: Bubble up some sort of configuration error? 19 | return true; 20 | } 21 | 22 | return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); 23 | } 24 | 25 | bool ULintRule_SoundWave_SampleRate::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 26 | { 27 | USoundWave* SoundWave = const_cast(CastChecked(ObjectToLint)); 28 | 29 | if (ValidSampleRates.Contains(SoundWave->GetSampleRateForCurrentPlatform())) 30 | { 31 | return true; 32 | } 33 | 34 | FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_SoundWave_SampleRate_Fix", "Please fix your sample rate of {0}."); 35 | RecommendedAction = FText::FormatOrdered(RecommendedAction, SoundWave->GetSampleRateForCurrentPlatform()); 36 | 37 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); 38 | 39 | return true; 40 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/LintWizard.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015-2017 by Gamemakin LLC 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Widgets/SCompoundWidget.h" 7 | #include "Widgets/Workflow/SWizard.h" 8 | #include "UI/SStepWidget.h" 9 | 10 | #include "LintReport.h" 11 | 12 | /** 13 | * 14 | */ 15 | class LINTER_API SLintWizard : public SCompoundWidget 16 | { 17 | public: 18 | SLATE_BEGIN_ARGS(SLintWizard) 19 | {} 20 | SLATE_END_ARGS() 21 | 22 | /** Constructs this widget with InArgs */ 23 | void Construct(const FArguments& InArgs); 24 | 25 | /** The wizard widget */ 26 | TSharedPtr MainWizard; 27 | 28 | /** The Lint Report widget */ 29 | TSharedPtr LintReport; 30 | 31 | /** The Linter combo box selection to use to select a linter rule set. */ 32 | TSharedPtr>> RuleSetSelectionComboBox; 33 | 34 | /** List of Linter managers grabbed from the Linter Module on widget creation */ 35 | TArray> RuleSets; 36 | 37 | /** List of maps in the project used for wizard purposes */ 38 | TArray> MapAssetDataList; 39 | 40 | /** Scrollbox for list of map assets in marketplace recommendation page */ 41 | TSharedPtr MarketplaceRecommendationMapScrollBoxPtr; 42 | 43 | /** Currently selected Linter Rule Set */ 44 | TSharedPtr SelectedRuleSet; 45 | 46 | bool bOfferPackage = false; 47 | EStepStatus FixUpRedirectorStatus = EStepStatus::Unknown; 48 | EStepStatus SaveAllStatus = EStepStatus::Unknown; 49 | 50 | void OnLintReportEntered(); 51 | void OnMarketplaceRecommendationsEntered(); 52 | 53 | bool LoadAssetsIfNeeded(const TArray& ObjectPaths, TArray& LoadedObjects); 54 | }; 55 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_NotTooBig.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Texture_Size_NotTooBig.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_Texture_Size_NotTooBig::ULintRule_Texture_Size_NotTooBig(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Texture_Size_NotTooBig::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | // If we aren't a texture, abort 15 | if (Cast(ObjectToLint) == nullptr) 16 | { 17 | // @TODO: Bubble up some sort of configuration error? 18 | return true; 19 | } 20 | 21 | return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); 22 | } 23 | 24 | bool ULintRule_Texture_Size_NotTooBig::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 25 | { 26 | const UTexture2D* Texture = CastChecked(ObjectToLint); 27 | 28 | int32 TexSizeX = Texture->GetSizeX(); 29 | int32 TexSizeY = Texture->GetSizeY(); 30 | 31 | // Check to see if textures are too big 32 | if (TexSizeX > MaxTextureSizeX || TexSizeY > MaxTextureSizeY) 33 | { 34 | FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_Texture_Size_NotTooBig_TooBig", "Please shrink your textures dimensions so that they fit within {0}x{1} pixels."); 35 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(RecommendedAction, MaxTextureSizeX, MaxTextureSizeY))); 36 | return false; 37 | } 38 | 39 | return true; 40 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Vars_EditableMustHaveTooltip.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | 7 | ULintRule_Blueprint_Vars_EditableMustHaveTooltip::ULintRule_Blueprint_Vars_EditableMustHaveTooltip(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Blueprint_Vars_EditableMustHaveTooltip::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 15 | 16 | bool bRuleViolated = false; 17 | 18 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsEditableMustHaveTooltip", "{Previous}{WhiteSpace}Please give variable {VarName} a tooltip as it is marked editable."); 19 | FText AllFixes; 20 | 21 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 22 | { 23 | FString PropName = Desc.VarName.ToString(); 24 | FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); 25 | 26 | if ((Desc.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) 27 | { 28 | if (!Desc.HasMetaData(FBlueprintMetadata::MD_Tooltip) || Desc.GetMetaData(FBlueprintMetadata::MD_Tooltip).Len() <= 0) 29 | { 30 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 31 | bRuleViolated = true; 32 | } 33 | } 34 | } 35 | 36 | if (bRuleViolated) 37 | { 38 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 39 | return false; 40 | } 41 | 42 | return true; 43 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/UI/SStepWidget.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "EditorStyleSet.h" 5 | #include "LinterStyle.h" 6 | #include "Widgets/DeclarativeSyntaxSupport.h" 7 | #include "Widgets/SCompoundWidget.h" 8 | #include "Types/SlateStructs.h" 9 | #include "Misc/ScopedSlowTask.h" 10 | 11 | 12 | enum EStepStatus { 13 | NoStatus, 14 | Unknown, 15 | InProgress, 16 | NeedsUpdate, 17 | Warning, 18 | Error, 19 | Success 20 | }; 21 | 22 | // Called when a step's action is invoked 23 | DECLARE_DELEGATE_OneParam(FOnStepPerformAction, FScopedSlowTask&); 24 | 25 | /* Widget that represents a 'step' or 'choice' in the PaCK wizard. */ 26 | class SStepWidget : public SCompoundWidget 27 | { 28 | public: 29 | 30 | SLATE_BEGIN_ARGS(SStepWidget) 31 | : _StepStatus(EStepStatus::NoStatus) 32 | , _ShowStepStatusIcon(true) 33 | { 34 | } 35 | 36 | /** Name to display for this step. */ 37 | SLATE_ATTRIBUTE(FText, StepName) 38 | 39 | /** Description to display for this step. */ 40 | SLATE_ATTRIBUTE(FText, StepDesc) 41 | 42 | /** Text to display within the action button for this step. */ 43 | SLATE_ATTRIBUTE(FText, StepActionText) 44 | 45 | /** Slate Brush to use as the thumbnail icon for this step. */ 46 | SLATE_ATTRIBUTE(const FSlateBrush*, Icon) 47 | 48 | /** Current status for this step. */ 49 | SLATE_ATTRIBUTE(EStepStatus, StepStatus) 50 | 51 | /** Current status for this step. */ 52 | SLATE_ATTRIBUTE(bool, ShowStepStatusIcon) 53 | 54 | /** Delegate to fire when this step's action is invoked. */ 55 | SLATE_EVENT(FOnStepPerformAction, OnPerformAction) 56 | 57 | SLATE_END_ARGS() 58 | 59 | public: 60 | TAttribute StepStatus; 61 | TAttribute StepActionText; 62 | FOnStepPerformAction OnPerformAction; 63 | TAttribute ShowStepStatusIcon; 64 | 65 | bool IsStepCompleted(bool bAllowWarning = true); 66 | 67 | void Construct(const FArguments& Args); 68 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_LooseNodes.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_LooseNodes.h" 3 | #include "LintRuleSet.h" 4 | #include "Sound/SoundWave.h" 5 | #include "Engine/Blueprint.h" 6 | #include "EdGraph/EdGraphPin.h" 7 | #include "EdGraph/EdGraphNode.h" 8 | #include "EdGraph/EdGraph.h" 9 | #include "EdGraphNode_Comment.h" 10 | #include "K2Node_Event.h" 11 | #include "K2Node_FunctionEntry.h" 12 | #include "K2Node_Knot.h" 13 | #include "K2Node_Tunnel.h" 14 | 15 | ULintRule_Blueprint_LooseNodes::ULintRule_Blueprint_LooseNodes(const FObjectInitializer& ObjectInitializer) 16 | : Super(ObjectInitializer) 17 | { 18 | } 19 | 20 | bool ULintRule_Blueprint_LooseNodes::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 21 | { 22 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 23 | 24 | // Check for loose nodes 25 | TArray Graphs; 26 | Blueprint->GetAllGraphs(Graphs); 27 | 28 | for (UEdGraph* Graph : Graphs) 29 | { 30 | for (UEdGraphNode* Node : Graph->Nodes) 31 | { 32 | if ( 33 | Node->IsAutomaticallyPlacedGhostNode() || 34 | Node->IsA(UK2Node_Event::StaticClass()) || 35 | Node->IsA(UK2Node_FunctionEntry::StaticClass()) || 36 | Node->IsA(UK2Node_Knot::StaticClass()) || 37 | Node->IsA(UEdGraphNode_Comment::StaticClass()) || 38 | Node->IsA(UK2Node_Tunnel::StaticClass()) 39 | ) 40 | { 41 | continue; 42 | } 43 | 44 | bool bNodeIsolated = true; 45 | 46 | TArray Pins = Node->GetAllPins(); 47 | for (UEdGraphPin* Pin : Pins) 48 | { 49 | if (Pin->LinkedTo.Num() != 0) 50 | { 51 | bNodeIsolated = false; 52 | break; 53 | } 54 | } 55 | 56 | if (bNodeIsolated) 57 | { 58 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); 59 | return false; 60 | } 61 | } 62 | } 63 | 64 | return true; 65 | 66 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/LintReportRuleError.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "UI/LintReportRuleError.h" 3 | #include "Widgets/Layout/SBorder.h" 4 | #include "Widgets/SBoxPanel.h" 5 | #include "Widgets/Layout/SExpandableArea.h" 6 | #include "Widgets/Input/SHyperlink.h" 7 | #include "LintRule.h" 8 | #include "Widgets/Views/SListView.h" 9 | #include "Widgets/Views/STableRow.h" 10 | #include "Widgets/Layout/SBox.h" 11 | 12 | #define LOCTEXT_NAMESPACE "LintReport" 13 | 14 | 15 | void SLintReportRuleError::Construct(const FArguments& Args) 16 | { 17 | RuleViolation = Args._RuleViolation; 18 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 19 | 20 | ChildSlot 21 | [ 22 | SNew(SVerticalBox) 23 | + SVerticalBox::Slot() 24 | .AutoHeight() 25 | .VAlign(VAlign_Center) 26 | .Padding(PaddingAmount) 27 | [ 28 | SNew(SHorizontalBox) 29 | + SHorizontalBox::Slot() 30 | .Padding(PaddingAmount) 31 | .AutoWidth() 32 | .HAlign(HAlign_Left) 33 | .VAlign(VAlign_Center) 34 | [ 35 | SNew(SHyperlink) 36 | .Text(FText::FromName(RuleViolation.Get()->ViolatorAssetData.PackageName)) 37 | .OnNavigate_Lambda([&]() 38 | { 39 | FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); 40 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); 41 | TArray AssetDatas; 42 | AssetDatas.Push(AssetRegistryModule.Get().GetAssetByObjectPath(RuleViolation.Get()->ViolatorAssetData.ObjectPath)); 43 | ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); 44 | }) 45 | ] 46 | ] 47 | + SVerticalBox::Slot() 48 | .AutoHeight() 49 | .VAlign(VAlign_Top) 50 | .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) 51 | [ 52 | SNew(STextBlock) 53 | .AutoWrapText(true) 54 | .Text(RuleViolation.Get()->RecommendedAction) 55 | .Visibility(RuleViolation.Get()->RecommendedAction.IsEmpty() ? EVisibility::Collapsed : EVisibility::SelfHitTestInvisible) 56 | ] 57 | ]; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRunner.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. 2 | #include "LintRunner.h" 3 | 4 | #define LOCTEXT_NAMESPACE "Linter" 5 | 6 | FCriticalSection FLintRunner::LintDataUpdateLock; 7 | 8 | FLintRunner::FLintRunner(UObject* InLoadedObject, const ULintRuleSet* LintRuleSet, TArray* InpOutRuleViolations, FScopedSlowTask* InParentScopedSlowTask) 9 | : LoadedObject(InLoadedObject) 10 | , RuleSet(LintRuleSet) 11 | , pOutRuleViolations(InpOutRuleViolations) 12 | , pLoadedRuleList(LintRuleSet != nullptr ? LintRuleSet->GetLintRuleListForClass(InLoadedObject->GetClass()) : nullptr) 13 | , ParentScopedSlowTask(InParentScopedSlowTask) 14 | { 15 | } 16 | 17 | bool FLintRunner::RequiresGamethread() 18 | { 19 | if (pLoadedRuleList != nullptr) 20 | { 21 | return pLoadedRuleList->RequiresGameThread(); 22 | } 23 | 24 | return false; 25 | } 26 | 27 | bool FLintRunner::Init() 28 | { 29 | if (LoadedObject == nullptr) 30 | { 31 | return false; 32 | } 33 | 34 | if (RuleSet == nullptr) 35 | { 36 | return false; 37 | } 38 | 39 | if (pLoadedRuleList == nullptr) 40 | { 41 | return false; 42 | } 43 | 44 | if (pOutRuleViolations == nullptr) 45 | { 46 | return false; 47 | } 48 | 49 | return true; 50 | } 51 | 52 | uint32 FLintRunner::Run() 53 | { 54 | if (LoadedObject == nullptr || pLoadedRuleList == nullptr || RuleSet == nullptr || pOutRuleViolations == nullptr) 55 | { 56 | return 2; 57 | } 58 | 59 | FString const AssetPath = LoadedObject->GetPathName(); 60 | UE_LOG(LogLinter, Display, TEXT("Loaded '%s'..."), *AssetPath); 61 | 62 | TArray RuleViolations; 63 | pLoadedRuleList->PassesRules(LoadedObject, RuleSet, RuleViolations); 64 | 65 | if (RuleViolations.Num() > 0) 66 | { 67 | FScopeLock lock(&LintDataUpdateLock); 68 | pOutRuleViolations->Append(RuleViolations); 69 | } 70 | 71 | UE_LOG(LogLinter, Display, TEXT("Finished '%s'..."), *AssetPath); 72 | return 0; 73 | } 74 | 75 | void FLintRunner::Stop() 76 | { 77 | return; // this runner doesn't have anything on-going so there is nothing to stop 78 | } 79 | 80 | void FLintRunner::Exit() 81 | { 82 | return; 83 | } 84 | 85 | #undef LOCTEXT_NAMESPACE 86 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_NonAtomic.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Vars_NonAtomic.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | #include "Internationalization/Regex.h" 7 | 8 | ULintRule_Blueprint_Vars_NonAtomic::ULintRule_Blueprint_Vars_NonAtomic(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | } 12 | 13 | 14 | bool ULintRule_Blueprint_Vars_NonAtomic::IsVariableAtomic(FBPVariableDescription& VarDesc) 15 | { 16 | return (VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean 17 | || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Byte 18 | || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Int 19 | || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Float 20 | || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_String 21 | || VarDesc.VarType.PinCategory == UEdGraphSchema_K2::PC_Enum 22 | ); 23 | } 24 | 25 | bool ULintRule_Blueprint_Vars_NonAtomic::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 26 | { 27 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 28 | 29 | bool bRuleViolated = false; 30 | 31 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsNonAtomic", "{Previous}{WhiteSpace}Please fix variable named {VarName}."); 32 | FText AllFixes; 33 | 34 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 35 | { 36 | FString PropName = Desc.VarName.ToString(); 37 | FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); 38 | 39 | if (IsVariableAtomic(Desc) && PropName.Contains(TypeName.ToString())) 40 | { 41 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 42 | bRuleViolated = true; 43 | } 44 | } 45 | 46 | if (bRuleViolated) 47 | { 48 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass())); 49 | return false; 50 | } 51 | 52 | return true; 53 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_RegEx.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Vars_Regex.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | #include "Internationalization/Regex.h" 7 | 8 | ULintRule_Blueprint_Vars_Regex::ULintRule_Blueprint_Vars_Regex(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | } 12 | 13 | 14 | bool ULintRule_Blueprint_Vars_Regex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 15 | { 16 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 17 | 18 | bool bRuleViolated = false; 19 | 20 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsRegex", "{Previous}{WhiteSpace}Please fix variable named {VarName}."); 21 | FText AllFixes; 22 | 23 | FString TestRegexPatternString = RegexPatternString; 24 | FString BoolTestRegexPatternString = TEXT("b") + RegexPatternString; 25 | 26 | const FRegexPattern TestRegexPattern = FRegexPattern(TestRegexPatternString); 27 | const FRegexPattern BoolTestRegexPattern = FRegexPattern(BoolTestRegexPatternString); 28 | 29 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 30 | { 31 | FString PropName = Desc.VarName.ToString(); 32 | FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); 33 | bool bIsBool = Desc.VarType.PinCategory == UEdGraphSchema_K2::PC_Boolean; 34 | 35 | FRegexMatcher Matcher(bIsBool ? BoolTestRegexPattern : TestRegexPattern, PropName); 36 | bool bFoundMatch = Matcher.FindNext(); 37 | 38 | if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) 39 | { 40 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 41 | bRuleViolated = true; 42 | } 43 | } 44 | 45 | if (bRuleViolated) 46 | { 47 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 48 | return false; 49 | } 50 | 51 | return true; 52 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Funcs_MustHaveReturn.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | #include "K2Node_FunctionResult.h" 7 | 8 | ULintRule_Blueprint_Funcs_MustHaveReturn::ULintRule_Blueprint_Funcs_MustHaveReturn(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | } 12 | 13 | bool ULintRule_Blueprint_Funcs_MustHaveReturn::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 14 | { 15 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 16 | 17 | // Early return out if blueprint type shouldn't be checked for return nodes 18 | switch (Blueprint->BlueprintType) 19 | { 20 | case BPTYPE_Normal: 21 | case BPTYPE_Const: 22 | case BPTYPE_LevelScript: 23 | case BPTYPE_FunctionLibrary: 24 | break; 25 | case BPTYPE_MacroLibrary: 26 | case BPTYPE_Interface: 27 | default: 28 | return true; 29 | } 30 | 31 | bool bRuleViolated = false; 32 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsMustHaveReturn", "{Previous}{WhiteSpace}Please give function {FuncName} a return node."); 33 | FText AllFixes; 34 | 35 | static const FName DefaultAnimGraphName("AnimGraph"); 36 | 37 | for (auto FunctionGraph : Blueprint->FunctionGraphs) 38 | { 39 | if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript 40 | && FunctionGraph->GetFName() != DefaultAnimGraphName) 41 | { 42 | TArray AllResultNodes; 43 | FunctionGraph->GetNodesOfClass(AllResultNodes); 44 | if (AllResultNodes.Num() <= 0) 45 | { 46 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 47 | bRuleViolated = true; 48 | } 49 | } 50 | } 51 | 52 | if (bRuleViolated) 53 | { 54 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 55 | return false; 56 | } 57 | 58 | return true; 59 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_StaticMesh_ValidUVs.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_StaticMesh_ValidUVs.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_StaticMesh_ValidUVs::ULintRule_StaticMesh_ValidUVs(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | //UStaticMesh::CheckLightMapUVs requires being ran on the game thread 11 | bRequiresGameThread = true; 12 | } 13 | 14 | bool ULintRule_StaticMesh_ValidUVs::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 15 | { 16 | // If we aren't a static mesh, abort 17 | if (Cast(ObjectToLint) == nullptr) 18 | { 19 | // @TODO: Bubble up some sort of configuration error? 20 | return true; 21 | } 22 | 23 | return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); 24 | } 25 | 26 | bool ULintRule_StaticMesh_ValidUVs::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 27 | { 28 | const UStaticMesh* StaticMesh = CastChecked(ObjectToLint); 29 | 30 | bool bHadErrors = false; 31 | 32 | TArray MissingUVs; 33 | TArray BadUVs; 34 | TArray ValidUVs; 35 | 36 | UStaticMesh::CheckLightMapUVs(const_cast(StaticMesh), MissingUVs, BadUVs, ValidUVs, true); 37 | 38 | if ((!bIgnoreMissingUVs && MissingUVs.Num() > 0)) 39 | { 40 | FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_StaticMesh_ValidUVs_Missing", "Static mesh has missing UVs. Please add at least one valid UV channel."); 41 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); 42 | return false; 43 | } 44 | 45 | if (BadUVs.Num() > 0) 46 | { 47 | FText RecommendedAction = NSLOCTEXT("Linter", "LintRule_StaticMesh_ValidUVs_Bad", "Static mesh has invalid UVs. [{0}]"); 48 | FText::FormatOrdered(RecommendedAction, FText::FromString(FString::Join(BadUVs, TEXT(", ")))); 49 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); 50 | return false; 51 | } 52 | 53 | return true; 54 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRuleSet.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "Misc/ScopedSlowTask.h" 6 | #include "LintRule.h" 7 | 8 | #include "LintRuleSet.generated.h" 9 | 10 | class ULinterNamingConvention; 11 | 12 | USTRUCT(BlueprintType) 13 | struct LINTER_API FLintRuleList 14 | { 15 | GENERATED_USTRUCT_BODY() 16 | 17 | FLintRuleList() 18 | {} 19 | 20 | UPROPERTY(EditAnywhere, Category = Default) 21 | TArray> LintRules; 22 | 23 | bool RequiresGameThread() const;; 24 | bool PassesRules(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; 25 | }; 26 | 27 | /** 28 | *Comment 29 | */ 30 | UCLASS(BlueprintType, Blueprintable) 31 | class LINTER_API ULintRuleSet : public UDataAsset 32 | { 33 | GENERATED_BODY() 34 | 35 | public: 36 | ULintRuleSet(const FObjectInitializer& ObjectInitializer); 37 | 38 | //UFUNCTION(BlueprintCallable, Category = "Conventions") 39 | const FLintRuleList* GetLintRuleListForClass(TSoftClassPtr Class) const; 40 | 41 | UFUNCTION(BlueprintCallable, Category = "Conventions") 42 | ULinterNamingConvention* GetNamingConvention() const; 43 | 44 | /** Invoke this with a list of asset paths to recursively lint all assets in paths. */ 45 | //UFUNCTION(BlueprintCallable, Category = "Lint") 46 | TArray LintPath(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask = nullptr) const; 47 | 48 | /** This is a temp dumb way to do this. */ 49 | TArray> LintPathShared(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask = nullptr) const; 50 | 51 | UPROPERTY(EditDefaultsOnly, Category = "Marketplace") 52 | bool bShowMarketplacePublishingInfoInLintWizard = false; 53 | 54 | UPROPERTY(EditDefaultsOnly, Category = "Rules") 55 | FText RuleSetDescription; 56 | 57 | UPROPERTY(EditDefaultsOnly, Category = "Commandlet") 58 | FString NameForCommandlet; 59 | 60 | protected: 61 | 62 | UPROPERTY(EditDefaultsOnly, Category = "Rules") 63 | TSoftObjectPtr NamingConvention; 64 | 65 | UPROPERTY(EditDefaultsOnly, Category = "Rules") 66 | TMap, FLintRuleList> ClassLintRulesMap; 67 | 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Funcs_PublicDescriptions.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | #include "K2Node_FunctionEntry.h" 7 | 8 | 9 | ULintRule_Blueprint_Funcs_PublicDescriptions::ULintRule_Blueprint_Funcs_PublicDescriptions(const FObjectInitializer& ObjectInitializer) 10 | : Super(ObjectInitializer) 11 | { 12 | } 13 | 14 | bool ULintRule_Blueprint_Funcs_PublicDescriptions::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 15 | { 16 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 17 | 18 | // Early return out if blueprint type shouldn't be checked for function descriptions 19 | switch (Blueprint->BlueprintType) 20 | { 21 | case BPTYPE_Normal: 22 | case BPTYPE_Const: 23 | case BPTYPE_LevelScript: 24 | case BPTYPE_FunctionLibrary: 25 | break; 26 | case BPTYPE_MacroLibrary: 27 | case BPTYPE_Interface: 28 | default: 29 | return true; 30 | } 31 | 32 | bool bRuleViolated = false; 33 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsPublicDescriptions", "{Previous}{WhiteSpace}Please give public function {FuncName} a description."); 34 | FText AllFixes; 35 | 36 | for (UEdGraph* FunctionGraph : Blueprint->FunctionGraphs) 37 | { 38 | if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) 39 | { 40 | UK2Node_FunctionEntry* FunctionEntryNode = nullptr; 41 | TArray EntryNodes; 42 | 43 | FunctionGraph->GetNodesOfClass(EntryNodes); 44 | 45 | if ((EntryNodes.Num() > 0) && EntryNodes[0]->IsEditable()) 46 | { 47 | FunctionEntryNode = Cast(EntryNodes[0]); 48 | } 49 | 50 | if (FunctionEntryNode != nullptr) 51 | { 52 | if (FUNC_AccessSpecifiers & FunctionEntryNode->GetFunctionFlags() & FUNC_Public) 53 | { 54 | if (FunctionEntryNode->MetaData.ToolTip.IsEmpty()) 55 | { 56 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("FuncName"), FText::FromString(FunctionGraph->GetName()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 57 | bRuleViolated = true; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | if (bRuleViolated) 65 | { 66 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 67 | return false; 68 | } 69 | 70 | return true; 71 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LinterNamingConvention.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "CoreMinimal.h" 5 | #include "UObject/Object.h" 6 | #include "Templates/SharedPointer.h" 7 | #include "IDetailCustomization.h" 8 | #include "PropertyHandle.h" 9 | #include "LinterNamingConvention.generated.h" 10 | 11 | 12 | /** 13 | * Class/Prefix/Suffix settings for Linter 14 | */ 15 | USTRUCT(BlueprintType) 16 | struct LINTER_API FLinterNamingConventionInfo 17 | { 18 | GENERATED_USTRUCT_BODY() 19 | 20 | FLinterNamingConventionInfo() 21 | : SoftClassPtr(nullptr) 22 | {} 23 | 24 | FLinterNamingConventionInfo(TSoftClassPtr InClass, FString InPrefix = TEXT(""), FString InSuffix = TEXT(""), FName InVariant = NAME_None) 25 | : SoftClassPtr(InClass) 26 | , Prefix(InPrefix) 27 | , Suffix(InSuffix) 28 | , Variant(InVariant) 29 | {} 30 | 31 | UPROPERTY(EditAnywhere, Category = Default, meta = (AllowAbstract = "")) 32 | TSoftClassPtr SoftClassPtr; 33 | 34 | UPROPERTY(EditAnywhere, Category = Default) 35 | FString Prefix; 36 | 37 | UPROPERTY(EditAnywhere, Category = Default) 38 | FString Suffix; 39 | 40 | UPROPERTY(EditAnywhere, Category = Default) 41 | FName Variant; 42 | }; 43 | 44 | class FLinterNamingConventionDetails : public IDetailCustomization 45 | { 46 | public: 47 | /** Makes a new instance of this detail layout class for a specific detail view requesting it */ 48 | static TSharedRef MakeInstance(); 49 | 50 | /** ILayoutDetails interface */ 51 | virtual void CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) override; 52 | 53 | void OnGenerateElementForDetails(TSharedRef StructProperty, int32 ElementIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout); 54 | }; 55 | 56 | /** 57 | * Contains a naming convention to be used by LinterManagers/LinterRules 58 | */ 59 | UCLASS(Abstract) 60 | class LINTER_API ULinterNamingConvention : public UDataAsset 61 | { 62 | GENERATED_BODY() 63 | 64 | public: 65 | 66 | ULinterNamingConvention(const FObjectInitializer& ObjectInitializer); 67 | 68 | UPROPERTY(EditAnywhere, Category="Conventions", meta = (AllowAbstract = "")) 69 | TArray ClassNamingConventions; 70 | 71 | UFUNCTION(BlueprintCallable, Category="Conventions") 72 | TArray GetNamingConventionsForClassVariant(TSoftClassPtr Class, FName Variant = NAME_None) const; 73 | 74 | UFUNCTION(Blueprintcallable, Category = "Conventions") 75 | void SortConventions(); 76 | 77 | protected: 78 | 79 | 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to Linter and Gamemakin LLC Style Guide Documentation 2 | 3 | This is the official documentation for Linter and the [Gamemakin LLC Style Guide](http://ue4.style). If you are looking for help or support, please try the [Gamemakin LLC Community Discord](http://discord.gamemak.in). 4 | 5 | ## About Linter 6 | 7 |
8 | 9 |
10 | 11 | Linter is an Unreal Engine 4 plugin available on the Unreal Engine Marketplace that can be used for automated scanning and reporting of a UE4 project's adherence to style guide standards. It can scan through all of your project's content using programmatic rulesets and tell you when you aren't following a rule. This is a fairly common type of tool usually seen in web development, but now we can do it with Unreal Engine! 12 | 13 | By default Linter is bundled with two rule sets: 14 | 15 | 1. The [Unreal Engine Marketplace Guidelines](https://www.unrealengine.com/marketplace-guidelines) 16 | 1. The [Gamemakin LLC Style Guide](http://ue4.style) 17 | 18 | ## About the Gamemakin LLC Style Guide 19 | 20 | While Linter is now capable of supporting multiple rule sets, Linter was originally developed with the Gamemakin LLC Style Guide in mind. This style guide is an opinionated set of guidelines written by [Michael Allar](http://www.twitter.com/michaelallar) for Unreal Engine 4 projects that continue to grow, change, and respond to newer and better organization patterns as well as community feedback. 21 | 22 | This style guide is not meant to be the definitive solution to all projects, but it is open-sourced in the hopes that those without style guides can use it as a starting point for their practices and try to come to a common consensus to many aspects of working with Unreal Engine 4. 23 | 24 | It can be found at the following URL: [http://ue4.style](http://ue4.style) 25 | 26 | ## About the Unreal Engine Marketplace Guidelines 27 | 28 | If you are creating content for the Unreal Engine Marketplace, your content must adhere to Epic's guidelines [which can be found on their site](https://www.unrealengine.com/marketplace-guidelines). 29 | 30 | Linter, Gamemakin LLC, and Michael Allar do not have any control or influence over these guidelines as well as whether your assets meet the required standards for the Unreal Engine Marketplace, but Linter is designed to help you conform to their rules by scanning your project against the rules they will be validated with. -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Vars_ConfigCategories.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Vars_ConfigCategories.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | 7 | ULintRule_Blueprint_Vars_ConfigCategories::ULintRule_Blueprint_Vars_ConfigCategories(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | } 11 | 12 | bool ULintRule_Blueprint_Vars_ConfigCategories::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 15 | 16 | bool bRuleViolated = false; 17 | 18 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintVarsConfigCategories", "{Previous}{WhiteSpace}Please give variable {VarName} a category."); 19 | FText FixTextTemplateEditable = NSLOCTEXT("Linter", "BlueprintVarsConfigCategoriesEditable", "{Previous}{WhiteSpace}Please give editable variable {VarName} a category starting with 'Config'."); 20 | FText AllFixes; 21 | 22 | int32 VariableCount = Blueprint->NewVariables.Num(); 23 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 24 | { 25 | if (FBlueprintEditorUtils::IsVariableComponent(Desc)) 26 | { 27 | VariableCount--; 28 | } 29 | } 30 | 31 | if (VariableCount < NumVariablesToRequireCategorization) 32 | { 33 | return true; 34 | } 35 | 36 | for (FBPVariableDescription Desc : Blueprint->NewVariables) 37 | { 38 | FString PropName = Desc.VarName.ToString(); 39 | FText TypeName = UEdGraphSchema_K2::TypeToText(Desc.VarType); 40 | 41 | // Is Editable variable? 42 | if ((Desc.PropertyFlags & CPF_DisableEditOnInstance) != CPF_DisableEditOnInstance) 43 | { 44 | if (!Desc.Category.ToString().StartsWith(TEXT("Config"))) 45 | { 46 | AllFixes = FText::FormatNamed(FixTextTemplateEditable, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 47 | bRuleViolated = true; 48 | continue; 49 | } 50 | } 51 | else 52 | { 53 | if (Desc.Category.IsEmptyOrWhitespace()) 54 | { 55 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("VarName"), FText::FromString(PropName), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 56 | bRuleViolated = true; 57 | } 58 | } 59 | } 60 | 61 | if (bRuleViolated) 62 | { 63 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 64 | return false; 65 | } 66 | 67 | return true; 68 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_NoTopLevel.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Path_NoTopLevel.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_Path_NoTopLevel::ULintRule_Path_NoTopLevel(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | ZeroTopLevelFoldersRecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_NoTopLevel_ZeroTopLevelFolders", "There appears to be no top level folders. Please put your assets in a top level folder."); 11 | PleaseUseThisFolderRecommendedAction = NSLOCTEXT("Linter", "LintRule_Path_NoTopLevel_PleaseUseThisFolder", "Please move this asset into a top level folder. Maybe \"{0}\"?"); 12 | } 13 | 14 | bool ULintRule_Path_NoTopLevel::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 15 | { 16 | FString PathName = ObjectToLint->GetPathName(); 17 | TArray PathElements; 18 | PathName.ParseIntoArray(PathElements, TEXT("/"), true); 19 | 20 | // Report issue with top assets not in a top level folder 21 | if (PathElements.Num() == 1) 22 | { 23 | FText RecommendedAction; 24 | 25 | // This is really slow to do for every single asset that fails to be in a top level folder 26 | // But doing it here makes the code base a lot cleaner and easier to follow for now. 27 | { 28 | // Determine content sub directory structure for project organization based rules 29 | TArray Subdirectories; 30 | IFileManager::Get().FindFiles(Subdirectories, *(FPaths::ProjectContentDir() / TEXT("*")), false, true); 31 | Subdirectories.Remove(TEXT("Collections")); 32 | Subdirectories.Remove(TEXT("Developers")); 33 | 34 | if (Subdirectories.Num() == 0) 35 | { 36 | RecommendedAction = ZeroTopLevelFoldersRecommendedAction; 37 | } 38 | else 39 | { 40 | FString MostPopulatedContentDir; 41 | int32 FileCount = 0; 42 | for (FString Subdirectory : Subdirectories) 43 | { 44 | TArray FileNames; 45 | IFileManager::Get().FindFilesRecursive(FileNames, *(FPaths::ProjectContentDir() / Subdirectory), TEXT("*"), true, false, false); 46 | if (FileNames.Num() > FileCount) 47 | { 48 | FileCount = FileNames.Num(); 49 | MostPopulatedContentDir = Subdirectory; 50 | } 51 | } 52 | 53 | RecommendedAction = FText::FormatOrdered(PleaseUseThisFolderRecommendedAction, FText::FromString(MostPopulatedContentDir)); 54 | } 55 | } 56 | 57 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); 58 | return false; 59 | } 60 | 61 | return true; 62 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Path_Regex.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Path_Regex.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | #include "Internationalization/Regex.h" 7 | 8 | ULintRule_Path_Regex::ULintRule_Path_Regex(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | , RegexPatternString(TEXT("[^a-zA-Z0-9_]")) 11 | { 12 | DisallowedPathElementRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_DisallowedPathElement", "Please rename \"{0}\" and remove disallowed characters."); 13 | NonConformingPathElementRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_NonConformingPathElement", "Please rename \"{0}\" and to conform to allowed characters."); 14 | 15 | DisallowedWholePathRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_DisallowedWholePath", "Please rename and remove disallowed characters."); 16 | NonConformingWholePathRecommendedAction = NSLOCTEXT("Linter", "ULintRule_Path_Regex_NonConformingWholePath", "Please rename and conform to allowed characters."); 17 | } 18 | 19 | bool ULintRule_Path_Regex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 20 | { 21 | FString PathName = ObjectToLint->GetPathName(); 22 | 23 | FRegexPattern RegexPattern = FRegexPattern(RegexPatternString); 24 | bool bRuleViolated = false; 25 | 26 | if (bCheckPerPathElement) 27 | { 28 | TArray PathElements; 29 | PathName.ParseIntoArray(PathElements, TEXT("/"), true); 30 | 31 | for (int32 i = 0; i < PathElements.Num() - 1; ++i) 32 | { 33 | FRegexMatcher RegexMatcher(RegexPattern, PathElements[i]); 34 | bool bFoundMatch = RegexMatcher.FindNext(); 35 | 36 | if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) 37 | { 38 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(bMustNotContainRegexPattern ? DisallowedPathElementRecommendedAction : NonConformingPathElementRecommendedAction, FText::FromString(PathElements[i])))); 39 | bRuleViolated = true; 40 | } 41 | } 42 | } 43 | else 44 | { 45 | FRegexMatcher RegexMatcher(RegexPattern, PathName); 46 | bool bFoundMatch = RegexMatcher.FindNext(); 47 | 48 | if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) 49 | { 50 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), FText::FormatOrdered(bMustNotContainRegexPattern ? DisallowedWholePathRecommendedAction : NonConformingWholePathRecommendedAction, FText::FromString(PathName)))); 51 | bRuleViolated = true; 52 | } 53 | } 54 | 55 | return !bRuleViolated; 56 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_ParticleSystem_EmitterNameRegex.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_ParticleSystem_EmitterNameRegex.h" 3 | #include "LintRuleSet.h" 4 | #include "Particles/ParticleEmitter.h" 5 | #include "Particles/ParticleSystem.h" 6 | #include "Internationalization/Regex.h" 7 | 8 | ULintRule_ParticleSystem_EmitterNameRegex::ULintRule_ParticleSystem_EmitterNameRegex(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | , RegexPatternString(TEXT("Particle Emitter")) 11 | { 12 | DisallowedRecommendedAction = NSLOCTEXT("Linter", "ULintRule_ParticleSystem_EmitterRegexName_Disallowed", "Please rename the emitter \"{0}\" as you have multiple emitters."); 13 | NonConformingRecommendedAction = NSLOCTEXT("Linter", "ULintRule_ParticleSystem_EmitterRegexName_NonConforming", "Please rename \"{0}\" as this emitter has invalid characters."); 14 | } 15 | 16 | bool ULintRule_ParticleSystem_EmitterNameRegex::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 17 | { 18 | // If we aren't a particle system, abort 19 | if (Cast(ObjectToLint) == nullptr) 20 | { 21 | // @TODO: Bubble up some sort of configuration error? 22 | return true; 23 | } 24 | 25 | return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); 26 | } 27 | 28 | bool ULintRule_ParticleSystem_EmitterNameRegex::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 29 | { 30 | const UParticleSystem* ParticleSystem = CastChecked(ObjectToLint); 31 | 32 | bool bRuleViolated = false; 33 | 34 | FText FixTextTemplate = NSLOCTEXT("Linter", "ParticleHasBadEmitterNames", "{Previous}{WhiteSpace}Please rename emitter {EmitterName}."); 35 | FText AllFixes; 36 | 37 | if (ParticleSystem->Emitters.Num() >= MinEmittersNeededToEnforce) 38 | { 39 | FRegexPattern RegexPattern = FRegexPattern(RegexPatternString); 40 | 41 | for (UParticleEmitter* Emitter : ParticleSystem->Emitters) 42 | { 43 | FRegexMatcher RegexMatcher(RegexPattern, Emitter->EmitterName.ToString()); 44 | bool bFoundMatch = RegexMatcher.FindNext(); 45 | 46 | if ((bFoundMatch && bMustNotContainRegexPattern) || (!bFoundMatch && !bMustNotContainRegexPattern)) 47 | { 48 | AllFixes = FText::FormatNamed(FixTextTemplate, TEXT("Previous"), AllFixes, TEXT("EmitterName"), FText::FromString(Emitter->EmitterName.ToString()), TEXT("WhiteSpace"), bRuleViolated ? FText::FromString(TEXT("\r\n")) : FText::GetEmpty()); 49 | bRuleViolated = true; 50 | } 51 | } 52 | } 53 | 54 | if (bRuleViolated) 55 | { 56 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 57 | return false; 58 | } 59 | 60 | return true; 61 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Texture_Size_PowerOfTwo.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Texture_Size_PowerOfTwo.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | #include "HAL/FileManager.h" 6 | 7 | ULintRule_Texture_Size_PowerOfTwo::ULintRule_Texture_Size_PowerOfTwo(const FObjectInitializer& ObjectInitializer) 8 | : Super(ObjectInitializer) 9 | { 10 | IgnoreTexturesInTheseGroups.Add(TextureGroup::TEXTUREGROUP_UI); 11 | } 12 | 13 | bool ULintRule_Texture_Size_PowerOfTwo::PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 14 | { 15 | // If we aren't a texture, abort 16 | if (Cast(ObjectToLint) == nullptr) 17 | { 18 | // @TODO: Bubble up some sort of configuration error? 19 | return true; 20 | } 21 | 22 | // If we're to ignore this texture LOD group, abort 23 | if (IgnoreTexturesInTheseGroups.Contains(Cast(ObjectToLint)->LODGroup)) 24 | { 25 | return true; 26 | } 27 | 28 | return Super::PassesRule(ObjectToLint, ParentRuleSet, OutRuleViolations); 29 | } 30 | 31 | bool ULintRule_Texture_Size_PowerOfTwo::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 32 | { 33 | const UTexture2D* Texture = CastChecked(ObjectToLint); 34 | 35 | int32 TexSizeX = Texture->GetSizeX(); 36 | int32 TexSizeY = Texture->GetSizeY(); 37 | 38 | bool bXFail = ((TexSizeX & (TexSizeX - 1)) != 0); 39 | bool bYFail = ((TexSizeY & (TexSizeY - 1)) != 0); 40 | 41 | UEnum* TextureGroupEnum = StaticEnum(); 42 | FString IgnoredLODGroupNames; 43 | 44 | for (TEnumAsByte LODGroup : IgnoreTexturesInTheseGroups) 45 | { 46 | IgnoredLODGroupNames += TextureGroupEnum->GetMetaData(TEXT("DisplayName"), LODGroup) + TEXT(", "); 47 | } 48 | IgnoredLODGroupNames.RemoveFromEnd(TEXT(", ")); 49 | 50 | 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(); 51 | 52 | if (bXFail || bYFail) 53 | { 54 | FText RecommendedAction; 55 | if (bXFail && bYFail) 56 | { 57 | 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); 58 | } 59 | else if (bXFail) 60 | { 61 | RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_X", "Please fix the width of this texture, currently {0}{1}"), TexSizeX, IgnoredLODGroupTip); 62 | } 63 | else if (bYFail) 64 | { 65 | RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "LintRule_Texture_Size_PowerOfTwo_Fail_Y", "Please fix the height of this texture, currently {0}{1}"), TexSizeY, IgnoredLODGroupTip); 66 | } 67 | 68 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); 69 | return false; 70 | } 71 | 72 | return true; 73 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/BatchRenameTool/BatchRenameTool.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "EditorStyleSet.h" 6 | #include "Widgets/Input/SEditableTextBox.h" 7 | #include "Widgets/Input/SCheckBox.h" 8 | #include "Widgets/SWindow.h" 9 | #include "Widgets/SUserWidget.h" 10 | #include "Widgets/SCompoundWidget.h" 11 | #include "Widgets/Layout/SUniformGridPanel.h" 12 | #include "Widgets/Layout/SSeparator.h" 13 | 14 | #define LOCTEXT_NAMESPACE "LinterBatchRenamer" 15 | 16 | /** 17 | * FDlgBatchRenameTool 18 | * 19 | * Wrapper class for SDlgBatchRenameTool. This class creates and launches a dialog then awaits the 20 | * result to return to the user. 21 | */ 22 | class FDlgBatchRenameTool 23 | { 24 | public: 25 | enum EResult 26 | { 27 | Cancel = 0, // No/Cancel, normal usage would stop the current action 28 | Confirm = 1, // Yes/Ok/Etc, normal usage would continue with action 29 | }; 30 | 31 | FDlgBatchRenameTool(const TArray Assets); 32 | 33 | /** Shows the dialog box and waits for the user to respond. */ 34 | EResult ShowModal(); 35 | 36 | FString Prefix; 37 | FString Suffix; 38 | bool bRemovePrefix; 39 | bool bRemoveSuffix; 40 | 41 | FString Find; 42 | FString Replace; 43 | 44 | private: 45 | 46 | /** Cached pointer to the modal window */ 47 | TSharedPtr DialogWindow; 48 | 49 | /** Cached pointer to the batch rename tool widget */ 50 | TSharedPtr DialogWidget; 51 | 52 | const TArray SelectedAssets; 53 | }; 54 | 55 | /** 56 | * Slate panel for batch renaming 57 | */ 58 | class SDlgBatchRenameTool : public SCompoundWidget 59 | { 60 | public: 61 | 62 | SLATE_BEGIN_ARGS(SDlgBatchRenameTool) 63 | {} 64 | /** Window in which this widget resides */ 65 | SLATE_ATTRIBUTE(TSharedPtr, ParentWindow) 66 | SLATE_END_ARGS() 67 | 68 | /** 69 | * Constructs this widget 70 | * 71 | * @param InArgs The declaration data for this widget 72 | */ 73 | void Construct(const FArguments& InArgs); 74 | 75 | /** 76 | * Returns the EResult of the button which the user pressed. Closing of the dialog 77 | * in any other way than clicking "Ok" results in this returning a "Cancel" value 78 | */ 79 | FDlgBatchRenameTool::EResult GetUserResponse() const; 80 | 81 | private: 82 | 83 | /** 84 | * Handles when a button is pressed, should be bound with appropriate EResult Key 85 | * 86 | * @param ButtonID - The return type of the button which has been pressed. 87 | */ 88 | FReply OnButtonClick(FDlgBatchRenameTool::EResult ButtonID) 89 | { 90 | ParentWindow->RequestDestroyWindow(); 91 | UserResponse = ButtonID; 92 | 93 | return FReply::Handled(); 94 | } 95 | 96 | /** Stores the users response to this dialog */ 97 | FDlgBatchRenameTool::EResult UserResponse; 98 | 99 | /** Pointer to the window which holds this Widget, required for modal control */ 100 | TSharedPtr ParentWindow; 101 | 102 | public: 103 | 104 | TSharedPtr PrefixTextBox; 105 | TSharedPtr SuffixTextBox; 106 | TSharedPtr FindTextBox; 107 | TSharedPtr ReplaceTextBox; 108 | TSharedPtr PrefixRemoveBox; 109 | TSharedPtr SuffixRemoveBox; 110 | }; 111 | 112 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/LintReportAssetError.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "UI/LintReportAssetError.h" 3 | #include "Widgets/Layout/SBorder.h" 4 | #include "Widgets/SBoxPanel.h" 5 | #include "Widgets/Layout/SExpandableArea.h" 6 | #include "Widgets/Input/SHyperlink.h" 7 | #include "LintRule.h" 8 | #include "Widgets/Views/SListView.h" 9 | #include "Widgets/Views/STableRow.h" 10 | #include "Widgets/Layout/SBox.h" 11 | 12 | #define LOCTEXT_NAMESPACE "LintReport" 13 | 14 | 15 | void SLintReportAssetError::Construct(const FArguments& Args) 16 | { 17 | RuleViolation = Args._RuleViolation; 18 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 19 | 20 | ULintRule* LintRule = RuleViolation.Get()->ViolatedRule.Get()->GetDefaultObject(); 21 | check(LintRule != nullptr); 22 | 23 | const FSlateBrush* RuleIcon = nullptr; 24 | bool bHasURL = !LintRule->RuleURL.IsEmpty(); 25 | 26 | switch (LintRule->RuleSeverity) 27 | { 28 | case ELintRuleSeverity::Error: 29 | RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Error"); 30 | break; 31 | case ELintRuleSeverity::Warning: 32 | RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Warning"); 33 | break; 34 | case ELintRuleSeverity::Info: 35 | RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Info"); 36 | break; 37 | //case ELintRuleSeverity::Ignore: 38 | default: 39 | break; 40 | } 41 | 42 | ChildSlot 43 | [ 44 | SNew(SVerticalBox) 45 | + SVerticalBox::Slot() 46 | .AutoHeight() 47 | .VAlign(VAlign_Center) 48 | .Padding(PaddingAmount) 49 | [ 50 | SNew(SHorizontalBox) 51 | + SHorizontalBox::Slot() 52 | .Padding(PaddingAmount) 53 | .AutoWidth() 54 | .VAlign(VAlign_Center) 55 | .HAlign(HAlign_Left) 56 | [ 57 | SNew(SBox) 58 | .WidthOverride(14.0f) 59 | .HeightOverride(14.0f) 60 | [ 61 | SNew(SImage) 62 | .Image(RuleIcon) 63 | ] 64 | ] 65 | + SHorizontalBox::Slot() 66 | .Padding(PaddingAmount) 67 | .AutoWidth() 68 | .HAlign(HAlign_Left) 69 | .VAlign(VAlign_Center) 70 | [ 71 | SNew(STextBlock) 72 | .Text(LintRule->RuleTitle) 73 | .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") 74 | ] 75 | + SHorizontalBox::Slot() 76 | .AutoWidth() 77 | .HAlign(HAlign_Left) 78 | .VAlign(VAlign_Center) 79 | [ 80 | SNew(SBox) 81 | .WidthOverride(16.0f) 82 | .HeightOverride(16.0f) 83 | [ 84 | SNew(SImage) 85 | .Cursor(EMouseCursor::Hand) 86 | .Visibility(bHasURL ? EVisibility::Visible : EVisibility::Collapsed) 87 | .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { FPlatformProcess::LaunchURL(*RuleViolation.Get()->ViolatedRule.Get()->GetDefaultObject()->RuleURL, NULL, NULL); return FReply::Handled(); }) 88 | .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) 89 | ] 90 | ] 91 | ] 92 | + SVerticalBox::Slot() 93 | .AutoHeight() 94 | .VAlign(VAlign_Top) 95 | .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) 96 | [ 97 | SNew(STextBlock) 98 | .AutoWrapText(true) 99 | .Text(LintRule->RuleDescription) 100 | ] 101 | + SVerticalBox::Slot() 102 | .AutoHeight() 103 | .VAlign(VAlign_Top) 104 | .Padding(20.0f, PaddingAmount, PaddingAmount, PaddingAmount) 105 | [ 106 | SNew(STextBlock) 107 | .AutoWrapText(true) 108 | .Text(RuleViolation.Get()->RecommendedAction) 109 | ] 110 | ]; 111 | } 112 | 113 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LinterBase.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | #include "Internationalization/Regex.h" 5 | #include "CoreMinimal.h" 6 | #include "UObject/Class.h" 7 | #include "UObject/Object.h" 8 | 9 | /** Future Notes: 10 | * The data structure for Linter was originally pretty straight forward. Now that we are introducing 11 | * some 'smart' behavior to some of the errors i.e. arbitrary automated resolve actions 12 | * and also tabulating data in both asset first and rule first orders, instead of being a 13 | * set of loosely defined rules, rules should ideally become a strongly typed const table of some kind. 14 | * This would simplify a lot of the data and structs being used here. -- Allar 15 | **/ 16 | 17 | struct LINTER_API FLinterAssetError 18 | { 19 | /** User friendly error message ready for displaying. */ 20 | FText ErrorMessage; 21 | 22 | /** URL Link to link user to if they want more information on this error. */ 23 | FString URLLink; 24 | 25 | FLinterAssetError(FText InErrorMessage, FString InURLLink = TEXT("")) 26 | : ErrorMessage(InErrorMessage) 27 | , URLLink(InURLLink) 28 | { 29 | } 30 | }; 31 | 32 | struct LINTER_API FLinterAssetErrorList 33 | { 34 | FString AssetName; 35 | FString AssetPath; 36 | FString SuggestedAssetName; 37 | TArray> Errors; 38 | TArray> Warnings; 39 | 40 | 41 | FLinterAssetErrorList() {}; 42 | 43 | FLinterAssetErrorList(FString InAssetName, FString InAssetPath, TArray> InErrors, TArray> InWarnings, FString InSuggestedAssetName = TEXT("")) 44 | : AssetName(InAssetName) 45 | , AssetPath(InAssetPath) 46 | , SuggestedAssetName(InSuggestedAssetName) 47 | , Errors(InErrors) 48 | , Warnings(InWarnings) 49 | { 50 | } 51 | 52 | FLinterAssetErrorList(const UObject* Object, TArray> InErrors, TArray> InWarnings, FString InSuggestedAssetName = TEXT("")) 53 | : AssetName(Object->GetName()) 54 | , AssetPath(Object->GetPathName()) 55 | , SuggestedAssetName(InSuggestedAssetName) 56 | , Errors(InErrors) 57 | , Warnings(InWarnings) 58 | 59 | { 60 | } 61 | 62 | void Reset() 63 | { 64 | AssetName.Empty(); 65 | AssetPath.Empty(); 66 | SuggestedAssetName.Empty(); 67 | Errors.Empty(); 68 | Warnings.Empty(); 69 | } 70 | 71 | bool IsEmpty() 72 | { 73 | return Errors.Num() == 0 && Warnings.Num() == 0; 74 | } 75 | 76 | bool HasErrors() 77 | { 78 | return Errors.Num() != 0; 79 | } 80 | }; 81 | 82 | /* Linter was originally built to store its data on a per-asset basis. 83 | * After Epic's purchase, UI needed a way to store data on a per-rule basis. 84 | * The following structs help out the LinterManager populate a per-rule list after Linting is complete. */ 85 | 86 | struct LINTER_API FLinterAssetInfo 87 | { 88 | FString AssetName; 89 | FString AssetPath; 90 | FString SuggestedAssetName; 91 | 92 | FText RuleErrorContext; 93 | 94 | FLinterAssetInfo(const UObject* Object, FText InRuleErrorContext = FText::GetEmpty(), FString InSuggestedAssetName = TEXT("")) 95 | { 96 | AssetName = Object->GetName(); 97 | AssetPath = Object->GetPathName(); 98 | SuggestedAssetName = InSuggestedAssetName; 99 | } 100 | }; 101 | 102 | struct LINTER_API FLinterRuleErrorList 103 | { 104 | FText RuleMessage; 105 | FString RuleURL; 106 | bool bWarning; 107 | TArray> AssetInfos; 108 | 109 | FLinterRuleErrorList() 110 | : bWarning(false) 111 | { 112 | } 113 | 114 | FLinterRuleErrorList(FText InRuleMessage, FText InRuleContext, bool bInWarning, const UObject* Object, FString InRuleURL, FString InSuggestedAssetName = TEXT("")) 115 | : RuleMessage(InRuleMessage) 116 | , RuleURL(InRuleURL) 117 | , bWarning(bInWarning) 118 | { 119 | AssetInfos.Add(MakeShareable(new FLinterAssetInfo(Object, InRuleContext, InSuggestedAssetName))); 120 | } 121 | }; -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_Blueprint_Funcs_MaxNodes.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_Blueprint_Funcs_MaxNodes.h" 3 | #include "LintRuleSet.h" 4 | #include "Engine/Blueprint.h" 5 | #include "EdGraphSchema_K2.h" 6 | #include "K2Node_FunctionResult.h" 7 | #include "K2Node_Knot.h" 8 | #include "K2Node_FunctionEntry.h" 9 | #include "K2Node_Self.h" 10 | #include "K2Node_DynamicCast.h" 11 | #include "K2Node_BreakStruct.h" 12 | #include "EdGraphNode_Comment.h" 13 | #include "K2Node_VariableGet.h" 14 | #include "K2Node_StructMemberGet.h" 15 | #include "K2Node_Tunnel.h" 16 | #include "K2Node_TemporaryVariable.h" 17 | #include "K2Node_FunctionTerminator.h" 18 | #include "K2Node_CastByteToEnum.h" 19 | #include "K2Node_CallFunction.h" 20 | 21 | ULintRule_Blueprint_Funcs_MaxNodes::ULintRule_Blueprint_Funcs_MaxNodes(const FObjectInitializer& ObjectInitializer) 22 | : Super(ObjectInitializer) 23 | { 24 | } 25 | 26 | bool ULintRule_Blueprint_Funcs_MaxNodes::IsNodeTrivial(const UEdGraphNode* Node) 27 | { 28 | if (Node->IsA(UK2Node_Knot::StaticClass()) 29 | || Node->IsA(UK2Node_FunctionEntry::StaticClass()) 30 | || Node->IsA(UK2Node_Self::StaticClass()) 31 | || Node->IsA(UK2Node_DynamicCast::StaticClass()) 32 | || Node->IsA(UK2Node_BreakStruct::StaticClass()) 33 | || Node->IsA(UEdGraphNode_Comment::StaticClass()) 34 | || Node->IsA(UK2Node_VariableGet::StaticClass()) 35 | || Node->IsA(UK2Node_StructMemberGet::StaticClass()) 36 | || Node->IsA(UK2Node_Tunnel::StaticClass()) 37 | || Node->IsA(UK2Node_TemporaryVariable::StaticClass()) 38 | || Node->IsA(UK2Node_FunctionResult::StaticClass()) 39 | || Node->IsA(UK2Node_FunctionTerminator::StaticClass()) 40 | || Node->IsA(UK2Node_CastByteToEnum::StaticClass()) 41 | ) 42 | { 43 | return true; 44 | } 45 | 46 | if (const UK2Node_CallFunction* CallFuncNode = Cast(Node)) 47 | { 48 | FName FuncName = CallFuncNode->FunctionReference.GetMemberName(); 49 | if (FuncName == TEXT("Conv_InterfaceToObject") 50 | || FuncName.ToString().Contains(TEXT("MakeLiteral")) 51 | || FuncName.ToString().Contains(TEXT("ToString")) 52 | || FuncName.ToString().StartsWith(TEXT("Make")) 53 | || FuncName.ToString().StartsWith(TEXT("Break")) 54 | ) 55 | { 56 | return true; 57 | } 58 | } 59 | 60 | return false; 61 | } 62 | 63 | bool ULintRule_Blueprint_Funcs_MaxNodes::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 64 | { 65 | UBlueprint* Blueprint = CastChecked(ObjectToLint); 66 | 67 | bool bRuleViolated = false; 68 | FText FixTextTemplate = NSLOCTEXT("Linter", "BlueprintFuncsMaxNodes", "{Previous}{WhiteSpace}Please simply function {FuncName} as it has {Nodes} nodes when we want a max of {MaxNodes}."); 69 | FText AllFixes; 70 | 71 | for (auto FunctionGraph : Blueprint->FunctionGraphs) 72 | { 73 | if (FunctionGraph->GetFName() != UEdGraphSchema_K2::FN_UserConstructionScript) 74 | { 75 | // If initial graph check exceeds node limit, filter out nodes that do not contribute to complexity 76 | if (FunctionGraph->Nodes.Num() > MaxExpectedNonTrivialNodes) 77 | { 78 | auto NodesCopy = FunctionGraph->Nodes; 79 | NodesCopy.RemoveAll([this](UEdGraphNode* Val) { return IsNodeTrivial(Val); }); 80 | 81 | // If removing knots and comments still exceeds node limit, report error 82 | if (NodesCopy.Num() > MaxExpectedNonTrivialNodes) 83 | { 84 | 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()); 85 | bRuleViolated = true; 86 | } 87 | } 88 | } 89 | } 90 | 91 | if (bRuleViolated) 92 | { 93 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), AllFixes)); 94 | return false; 95 | } 96 | 97 | return true; 98 | } -------------------------------------------------------------------------------- /Plugins/Linter/Source/MarketplaceLinter/Private/MarketplaceNamingConvention.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | #include "MarketplaceNamingConvention.h" 3 | 4 | 5 | UMarketplaceNamingConvention::UMarketplaceNamingConvention(const FObjectInitializer& ObjectInitializer) 6 | : Super(ObjectInitializer) 7 | { 8 | #define SCRIPT_PATH(ScriptPath) TSoftClassPtr(FSoftObjectPath(TEXT("/Script/" #ScriptPath))) 9 | #define ADD_PREFIX(ClassName, Prefix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix)); 10 | #define ADD_PREFIX_SUFFIX(ClassName, Prefix, Suffix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix, Suffix)); 11 | 12 | // Animation 13 | ADD_PREFIX(AimOffsetBlendSpace, "AO_"); 14 | ADD_PREFIX(AimOffsetBlendSpace1D, "AO_"); 15 | ADD_PREFIX(AnimBlueprint, "ABP_"); 16 | ADD_PREFIX(AnimComposite, "AC_"); 17 | ADD_PREFIX(AnimMontage, "AM_"); 18 | ADD_PREFIX(AnimSequence, "A_"); 19 | ADD_PREFIX(BlendSpace, "BS_"); 20 | ADD_PREFIX(BlendSpace1D, "BS_"); 21 | ADD_PREFIX(MorphTarget, "MT_"); 22 | ADD_PREFIX(Rig, "Rig_"); 23 | ADD_PREFIX(SkeletalMesh, "SK_"); 24 | ADD_PREFIX(Skeleton, "SKEL_"); 25 | 26 | 27 | // Artificial Intelligence 28 | ADD_PREFIX(AIController, "AIC_"); 29 | ADD_PREFIX(BehaviorTree, "BT_"); 30 | ADD_PREFIX(BlackboardData, "BB_"); 31 | ADD_PREFIX(BTDecorator, "BTDecorator_"); 32 | ADD_PREFIX(BTService, "BTService_"); 33 | ADD_PREFIX(BTTaskNode, "BTTask_"); 34 | 35 | // Blueprints 36 | ADD_PREFIX(Blueprint, "BP_"); 37 | ADD_PREFIX(BlueprintFunctionLibrary, "BPFL_"); 38 | ADD_PREFIX(Interface, "BPI_"); 39 | ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("IntroTutorials.EditorTutorial"), "TBP_")); 40 | ADD_PREFIX(UserDefinedEnum, "E"); 41 | ADD_PREFIX(UserDefinedStruct, "F"); 42 | 43 | // Materials 44 | ADD_PREFIX(Material, "M_"); 45 | ADD_PREFIX(Material, "MA_"); 46 | ADD_PREFIX(Material, "MAT_"); 47 | ADD_PREFIX(MaterialFunction, "MF_"); 48 | ADD_PREFIX(MaterialInstance, "MI_"); 49 | ADD_PREFIX(MaterialInstanceConstant, "MI_"); 50 | ADD_PREFIX(MaterialParameterCollection, "MPC_"); 51 | ADD_PREFIX(SubsurfaceProfile, "SP_"); 52 | 53 | // Textures 54 | ADD_PREFIX(Texture2D, "T_"); 55 | ADD_PREFIX(TextureCube, "TC_"); 56 | ADD_PREFIX(TextureRenderTarget2D, "RT_"); 57 | ADD_PREFIX(TextureRenderTargetCube, "RTC_"); 58 | ADD_PREFIX(TextureLightProfile, "TLP_"); 59 | 60 | // Media 61 | ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaTexture"), "MT_")); 62 | ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaPlayer"), "MP_")); 63 | 64 | // Miscellaneous 65 | ADD_PREFIX(VectorFieldAnimated, "VFA_"); 66 | ADD_PREFIX(CameraAnim, "CA_"); 67 | ADD_PREFIX(CurveLinearColor, "Curve_"); 68 | ADD_PREFIX(CurveTable, "Curve_"); 69 | ADD_PREFIX(DataTable, "DT_"); 70 | ADD_PREFIX(CurveFloat, "Curve_"); 71 | ADD_PREFIX(ForceFeedbackEffect, "FFE_"); 72 | ADD_PREFIX(MatineeAnimInterface, "Matinee_"); 73 | ADD_PREFIX(ObjectLibrary, "OL_"); 74 | ADD_PREFIX(VectorFieldStatic, "VF_"); 75 | ADD_PREFIX(TouchInterface, "TI_"); 76 | ADD_PREFIX(CurveVector, "Curve_"); 77 | ADD_PREFIX(StaticMesh, "SM_"); 78 | ADD_PREFIX(StaticMesh, "S_"); 79 | 80 | // Paper 2D 81 | 82 | // Physics 83 | ADD_PREFIX(PhysicalMaterial, "PM_"); 84 | ADD_PREFIX(PhysicsAsset, "PHYS_"); 85 | 86 | // Sounds 87 | ADD_PREFIX(DialogueVoice, "DV_"); 88 | ADD_PREFIX(DialogueWave, "DW_"); 89 | ADD_PREFIX(ReverbEffect, "Reverb_"); 90 | ADD_PREFIX(SoundAttenuation, "ATT_"); 91 | ADD_PREFIX(SoundClass, ""); 92 | ADD_PREFIX(SoundConcurrency, "_SC"); 93 | ADD_PREFIX_SUFFIX(SoundCue, "A_", "_Cue"); 94 | ADD_PREFIX(SoundMix, "Mix_"); 95 | ADD_PREFIX(SoundWave, "A_"); 96 | 97 | // User Interface 98 | ADD_PREFIX(Font, "Font_"); 99 | ADD_PREFIX(SlateBrushAsset, "Brush_"); 100 | ADD_PREFIX(SlateWidgetStyleAsset, "Style_"); 101 | ADD_PREFIX(WidgetBlueprint, "WBP_"); 102 | 103 | // Effects 104 | ADD_PREFIX(ParticleSystem, "PS_"); 105 | 106 | #undef ADD_PREFIX 107 | 108 | SortConventions(); 109 | } 110 | 111 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRules/LintRule_IsNamedCorrectly_Base.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "LintRules/LintRule_IsNamedCorrectly_Base.h" 3 | #include "LintRuleSet.h" 4 | #include "LinterNamingConvention.h" 5 | 6 | ULintRule_IsNamedCorrectly_Base::ULintRule_IsNamedCorrectly_Base(const FObjectInitializer& ObjectInitializer) 7 | : Super(ObjectInitializer) 8 | { 9 | 10 | } 11 | 12 | bool ULintRule_IsNamedCorrectly_Base::PassesRule_Internal_Implementation(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 13 | { 14 | // NameSettingList Contributed by RocknRolla#3102 on http://discord.gamemak.in 15 | TArray NameSettingList; 16 | // If ObjectToLint is a Blueprint, it's class hierarchy does not contain actual classes that Blueprints extend (like Actor, Character, PlayerController, etc.). 17 | // So we try to use Blueprint's ParentClass to access it's class hierarchy. 18 | if (ObjectToLint->IsA()) 19 | { 20 | const TSubclassOf BlueprintClass = Cast(ObjectToLint)->ParentClass; 21 | if (BlueprintClass != nullptr) 22 | { 23 | NameSettingList = ParentRuleSet->GetNamingConvention()->GetNamingConventionsForClassVariant(TSoftClassPtr(BlueprintClass), GetRuleBasedObjectVariantName(ObjectToLint)); 24 | } 25 | } 26 | // If ObjectToLint is not a Blueprint or we failed to find any conventions, just fall back to our default algorithm. 27 | if (NameSettingList.Num() == 0) 28 | { 29 | NameSettingList = ParentRuleSet->GetNamingConvention()->GetNamingConventionsForClassVariant(ObjectToLint->GetClass(), GetRuleBasedObjectVariantName(ObjectToLint)); 30 | } 31 | 32 | // If we don't have a name rule for this type of asset, simply return true 33 | if (NameSettingList.Num() == 0) 34 | { 35 | return true; 36 | } 37 | 38 | bool bFoundMatchingNameRule = false; 39 | for (FLinterNamingConventionInfo Info : NameSettingList) 40 | { 41 | bool bPassesPrefixCheck = Info.Prefix.IsEmpty() ? true : ObjectToLint->GetName().StartsWith(Info.Prefix, ESearchCase::CaseSensitive); 42 | bool bPassesSuffixCheck = Info.Suffix.IsEmpty() ? true : ObjectToLint->GetName().EndsWith(Info.Suffix, ESearchCase::CaseSensitive); 43 | // Run prefix and suffix checks using found name settings if they are non-null 44 | 45 | if (bPassesPrefixCheck && bPassesSuffixCheck) 46 | { 47 | bFoundMatchingNameRule = true; 48 | break; 49 | } 50 | } 51 | 52 | if (!bFoundMatchingNameRule) 53 | { 54 | FString SuggestedName = BuildSuggestedName(ObjectToLint->GetName(), NameSettingList[0].Prefix, NameSettingList[0].Suffix); 55 | FText RecommendedAction = FText::FormatOrdered(NSLOCTEXT("Linter", "IsNamedCorrectly_RecommendedAction", "Recommended name: [{0}]."), FText::FromString(SuggestedName)); 56 | OutRuleViolations.Push(FLintRuleViolation(ObjectToLint, GetClass(), RecommendedAction)); 57 | return false; 58 | } 59 | 60 | // If we don't have name settings or passed all name checks, simply return true 61 | return true; 62 | } 63 | 64 | FString ULintRule_IsNamedCorrectly_Base::BuildSuggestedName(FString CurrentName, FString DesiredPrefix, FString DesiredSuffix /*= TEXT("")*/) 65 | { 66 | FString SuggestedName; 67 | 68 | int32 FirstUnderscore = CurrentName.Find(TEXT("_")); 69 | int32 LastUnderscore = CurrentName.Find(TEXT("_"), ESearchCase::IgnoreCase, ESearchDir::FromEnd); 70 | 71 | bool bAddPrefix = false; 72 | bool bAddSuffix = false; 73 | 74 | // Attempt to remove a bad prefix 75 | if (!DesiredPrefix.IsEmpty() && !CurrentName.StartsWith(DesiredPrefix, ESearchCase::CaseSensitive)) 76 | { 77 | bAddPrefix = true; 78 | if (FirstUnderscore <= 3) 79 | { 80 | CurrentName = CurrentName.RightChop(FirstUnderscore + 1); 81 | } 82 | } 83 | 84 | // Attempt to remove a bad suffix 85 | if (!DesiredSuffix.IsEmpty() && !CurrentName.EndsWith(DesiredSuffix, ESearchCase::CaseSensitive)) 86 | { 87 | bAddSuffix = true; 88 | if (CurrentName.Len() - LastUnderscore <= 3) 89 | { 90 | CurrentName = CurrentName.LeftChop(CurrentName.Len() - LastUnderscore); 91 | } 92 | } 93 | 94 | SuggestedName = CurrentName; 95 | 96 | if (bAddPrefix) 97 | { 98 | SuggestedName = DesiredPrefix + SuggestedName; 99 | } 100 | 101 | if (bAddSuffix) 102 | { 103 | SuggestedName = SuggestedName + DesiredSuffix; 104 | } 105 | 106 | return SuggestedName; 107 | } 108 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LinterNamingConvention.cpp: -------------------------------------------------------------------------------- 1 | #include "LinterNamingConvention.h" 2 | #include "DetailLayoutBuilder.h" 3 | #include "PropertyCustomizationHelpers.h" 4 | #include "Templates/SharedPointer.h" 5 | #include "DetailCategoryBuilder.h" 6 | #include "IDetailChildrenBuilder.h" 7 | 8 | TSharedRef FLinterNamingConventionDetails::MakeInstance() 9 | { 10 | return MakeShareable(new FLinterNamingConventionDetails()); 11 | } 12 | 13 | void FLinterNamingConventionDetails::CustomizeDetails(class IDetailLayoutBuilder& DetailBuilder) 14 | { 15 | // Edit the Conventions category 16 | IDetailCategoryBuilder& DetailCategory = DetailBuilder.EditCategory("Conventions"); 17 | TSharedRef NamingConventionsProperty = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(ULinterNamingConvention, ClassNamingConventions), ULinterNamingConvention::StaticClass()); 18 | 19 | TSharedRef ConventionsPropertyBuilder = MakeShareable(new FDetailArrayBuilder(NamingConventionsProperty)); 20 | ConventionsPropertyBuilder->OnGenerateArrayElementWidget(FOnGenerateArrayElementWidget::CreateSP(this, &FLinterNamingConventionDetails::OnGenerateElementForDetails, &DetailBuilder)); 21 | 22 | 23 | DetailCategory.AddCustomBuilder(ConventionsPropertyBuilder); 24 | } 25 | 26 | void FLinterNamingConventionDetails::OnGenerateElementForDetails(TSharedRef StructProperty, int32 ElementIndex, IDetailChildrenBuilder& ChildrenBuilder, IDetailLayoutBuilder* DetailLayout) 27 | { 28 | ChildrenBuilder.AddCustomRow(FText::GetEmpty()) 29 | [ 30 | SNew(SHorizontalBox) 31 | + SHorizontalBox::Slot() 32 | .FillWidth(1.0f) 33 | [ 34 | SNew(SProperty, StructProperty->GetChildHandle("SoftClassPtr")) 35 | .ShouldDisplayName(false) 36 | ] 37 | + SHorizontalBox::Slot() 38 | .FillWidth(0.25f) 39 | [ 40 | SNew(SProperty, StructProperty->GetChildHandle("Variant")) 41 | ] 42 | + SHorizontalBox::Slot() 43 | .FillWidth(0.25f) 44 | [ 45 | SNew(SProperty, StructProperty->GetChildHandle("Prefix")) 46 | ] 47 | + SHorizontalBox::Slot() 48 | .FillWidth(0.25f) 49 | [ 50 | SNew(SProperty, StructProperty->GetChildHandle("Suffix")) 51 | ] 52 | ]; 53 | } 54 | 55 | ULinterNamingConvention::ULinterNamingConvention(const FObjectInitializer& ObjectInitializer) 56 | : Super(ObjectInitializer) 57 | { 58 | ClassNamingConventions = TArray(); 59 | } 60 | 61 | TArray ULinterNamingConvention::GetNamingConventionsForClassVariant(TSoftClassPtr Class, FName Variant /*= NAME_None*/) const 62 | { 63 | TArray NamingConventionList; 64 | 65 | UClass* searchClass = Class.Get(); 66 | while (NamingConventionList.Num() == 0 && searchClass != nullptr) 67 | { 68 | NamingConventionList = ClassNamingConventions.FilterByPredicate([searchClass, Variant](const FLinterNamingConventionInfo& Info) 69 | { 70 | return (Info.SoftClassPtr.Get() == searchClass && Info.Variant == Variant); 71 | }); 72 | 73 | // Abort if we try to go above UObject 74 | if (searchClass == UObject::StaticClass()) 75 | { 76 | break; 77 | } 78 | 79 | // @HACK: Editor UI won't allow us to select the UObject class in some cases 80 | if (searchClass == UAnyObject_LinterDummyClass::StaticClass()) 81 | { 82 | searchClass = UObject::StaticClass(); 83 | continue; 84 | } 85 | 86 | // Load our parent class in case we failed to get naming conventions 87 | searchClass = searchClass->GetSuperClass(); 88 | } 89 | 90 | return NamingConventionList; 91 | } 92 | 93 | void ULinterNamingConvention::SortConventions() 94 | { 95 | ClassNamingConventions.Sort([](const FLinterNamingConventionInfo& A, const FLinterNamingConventionInfo& B) 96 | { 97 | if (A.SoftClassPtr.GetAssetName() < B.SoftClassPtr.GetAssetName()) 98 | { 99 | return true; 100 | } 101 | 102 | if (A.SoftClassPtr.GetAssetName() == B.SoftClassPtr.GetAssetName()) 103 | { 104 | int32 sort = A.Variant.Compare(B.Variant); 105 | if (sort < 0) 106 | { 107 | return true; 108 | } 109 | 110 | if (sort == 0) 111 | { 112 | sort = A.Prefix.Compare(B.Prefix); 113 | if (sort < 0) 114 | { 115 | return true; 116 | } 117 | 118 | if (sort == 0) 119 | { 120 | sort = A.Suffix.Compare(B.Suffix); 121 | if (sort <= 0) 122 | { 123 | return true; 124 | } 125 | return false; 126 | } 127 | 128 | return false; 129 | } 130 | 131 | return false; 132 | } 133 | 134 | return false; 135 | }); 136 | } 137 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/GamemakinLinter/Private/GamemakinNamingConvention.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "GamemakinNamingConvention.h" 3 | 4 | 5 | UGamemakinNamingConvention::UGamemakinNamingConvention(const FObjectInitializer& ObjectInitializer) 6 | : Super(ObjectInitializer) 7 | { 8 | #define SCRIPT_PATH(ScriptPath) TSoftClassPtr(FSoftObjectPath(TEXT("/Script/" #ScriptPath))) 9 | #define ADD_PREFIX(ClassName, Prefix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix)); 10 | #define ADD_PREFIX_SUFFIX(ClassName, Prefix, Suffix) ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine." #ClassName))), Prefix, Suffix)); 11 | 12 | // Animation 13 | ADD_PREFIX(AimOffsetBlendSpace, "AO_"); 14 | ADD_PREFIX(AimOffsetBlendSpace1D, "AO_"); 15 | ADD_PREFIX(AnimBlueprint, "ABP_"); 16 | ADD_PREFIX(AnimComposite, "AC_"); 17 | ADD_PREFIX(AnimMontage, "AM_"); 18 | ADD_PREFIX(AnimSequence, "A_"); 19 | ADD_PREFIX(BlendSpace, "BS_"); 20 | ADD_PREFIX(BlendSpace1D, "BS_"); 21 | ADD_PREFIX(MorphTarget, "MT_"); 22 | ADD_PREFIX(Rig, "Rig_"); 23 | ADD_PREFIX(SkeletalMesh, "SK_"); 24 | ADD_PREFIX(Skeleton, "SKEL_"); 25 | 26 | 27 | // Artificial Intelligence 28 | ADD_PREFIX(AIController, "AIC_"); 29 | ADD_PREFIX(BehaviorTree, "BT_"); 30 | ADD_PREFIX(BlackboardData, "BB_"); 31 | ADD_PREFIX(BTDecorator, "BTDecorator_"); 32 | ADD_PREFIX(BTService, "BTService_"); 33 | ADD_PREFIX(BTTaskNode, "BTTask_"); 34 | 35 | // Blueprints 36 | ADD_PREFIX(Blueprint, "BP_"); 37 | ADD_PREFIX(BlueprintFunctionLibrary, "BPFL_"); 38 | ADD_PREFIX(Interface, "BPI_"); 39 | ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("IntroTutorials.EditorTutorial"), "TBP_")); 40 | ADD_PREFIX(UserDefinedEnum, "E"); 41 | ADD_PREFIX(UserDefinedStruct, "F"); 42 | 43 | // Materials 44 | ADD_PREFIX(Material, "M_"); 45 | ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.Material"))), "PP_", TEXT(""), "PostProcess")); 46 | ADD_PREFIX(MaterialFunction, "MF_"); 47 | ADD_PREFIX(MaterialInstance, "MI_"); 48 | ADD_PREFIX(MaterialInstanceConstant, "MI_"); 49 | ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.MaterialInstance"))), "PPI_", TEXT(""), "PostProcess")); 50 | ClassNamingConventions.Push(FLinterNamingConventionInfo(TSoftClassPtr(FSoftObjectPath(TEXT("/Script/Engine.MaterialInstanceConstant"))), "PPI_", TEXT(""), "PostProcess")); 51 | ADD_PREFIX(MaterialParameterCollection, "MPC_"); 52 | ADD_PREFIX(SubsurfaceProfile, "SP_"); 53 | 54 | // Textures 55 | ADD_PREFIX(Texture2D, "T_"); 56 | ADD_PREFIX(TextureCube, "TC_"); 57 | ADD_PREFIX(TextureRenderTarget2D, "RT_"); 58 | ADD_PREFIX(TextureRenderTargetCube, "RTC_"); 59 | ADD_PREFIX(TextureLightProfile, "TLP_"); 60 | 61 | // Media 62 | ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaTexture"), "MT_")); 63 | ClassNamingConventions.Push(FLinterNamingConventionInfo(SCRIPT_PATH("MediaAssets.MediaPlayer"), "MP_")); 64 | 65 | // Miscellaneous 66 | ADD_PREFIX(VectorFieldAnimated, "VFA_"); 67 | ADD_PREFIX(CameraAnim, "CA_"); 68 | ADD_PREFIX(CurveLinearColor, "Curve_"); 69 | ADD_PREFIX(CurveTable, "Curve_"); 70 | ADD_PREFIX(DataTable, "DT_"); 71 | ADD_PREFIX(CurveFloat, "Curve_"); 72 | ADD_PREFIX(ForceFeedbackEffect, "FFE_"); 73 | ADD_PREFIX(MatineeAnimInterface, "Matinee_"); 74 | ADD_PREFIX(ObjectLibrary, "OL_"); 75 | ADD_PREFIX(VectorFieldStatic, "VF_"); 76 | ADD_PREFIX(TouchInterface, "TI_"); 77 | ADD_PREFIX(CurveVector, "Curve_"); 78 | ADD_PREFIX(StaticMesh, "S_"); 79 | 80 | // Paper 2D 81 | 82 | // Physics 83 | ADD_PREFIX(PhysicalMaterial, "PM_"); 84 | ADD_PREFIX(PhysicsAsset, "PHYS_"); 85 | 86 | // Sounds 87 | ADD_PREFIX(DialogueVoice, "DV_"); 88 | ADD_PREFIX(DialogueWave, "DW_"); 89 | ADD_PREFIX(ReverbEffect, "Reverb_"); 90 | ADD_PREFIX(SoundAttenuation, "ATT_"); 91 | ADD_PREFIX(SoundClass, ""); 92 | ADD_PREFIX(SoundConcurrency, "_SC"); 93 | ADD_PREFIX_SUFFIX(SoundCue, "A_", "_Cue"); 94 | ADD_PREFIX(SoundMix, "Mix_"); 95 | ADD_PREFIX(SoundWave, "A_"); 96 | 97 | // User Interface 98 | ADD_PREFIX(Font, "Font_"); 99 | ADD_PREFIX(SlateBrushAsset, "Brush_"); 100 | ADD_PREFIX(SlateWidgetStyleAsset, "Style_"); 101 | ADD_PREFIX(WidgetBlueprint, "WBP_"); 102 | 103 | // Effects 104 | ADD_PREFIX(ParticleSystem, "PS_"); 105 | 106 | #undef ADD_PREFIX 107 | 108 | SortConventions(); 109 | } 110 | 111 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/LintRule.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "CoreMinimal.h" 5 | #include "LintRule.generated.h" 6 | 7 | UENUM(BlueprintType) 8 | enum class ELintRuleSeverity : uint8 9 | { 10 | Error, 11 | Warning, 12 | Info 13 | // Ignore 14 | }; 15 | 16 | USTRUCT(BlueprintType) 17 | struct LINTER_API FLintRuleViolation 18 | { 19 | GENERATED_USTRUCT_BODY() 20 | 21 | FLintRuleViolation() 22 | : Violator(nullptr) 23 | , ViolatedRule(nullptr) 24 | , RecommendedAction(FText::GetEmpty()) 25 | { 26 | } 27 | 28 | FLintRuleViolation(UObject* InViolator, TSubclassOf InViolatedRule, const FText InRecommendedAction = FText::GetEmpty()) 29 | : Violator(InViolator) 30 | , ViolatedRule(InViolatedRule) 31 | , RecommendedAction(InRecommendedAction) 32 | { 33 | } 34 | 35 | // I don't particularly like this way of extracting relevant data, but alas here we are. 36 | static TArray AllRuleViolationsWithViolator(const TArray& RuleViolationCollection, const UObject* SearchViolator); 37 | static TArray> AllRuleViolationsWithViolatorShared(const TArray& RuleViolationCollection, const UObject* SearchViolator); 38 | static TArray> AllRuleViolationsWithViolatorShared(const TArray>& RuleViolationCollection, const UObject* SearchViolator); 39 | static TArray AllRuleViolationsOfSpecificRule(const TArray& RuleViolationCollection, TSubclassOf SearchRule); 40 | static TArray AllRuleViolationsOfRuleGroup(const TArray& RuleViolationCollection, FName SearchRuleGroup); 41 | 42 | static TArray AllRuleViolationViolators(const TArray& RuleViolationCollection); 43 | static TArray AllRuleViolationViolators(const TArray>& RuleViolationCollection); 44 | static TMultiMap AllRuleViolationsMappedByViolator(const TArray& RuleViolationCollection); 45 | static TMultiMap AllRuleViolationsMappedByViolatedLintRule(const TArray& RuleViolationCollection); 46 | static TMultiMap> AllRuleViolationsMappedByViolatedLintRuleShared(const TArray& RuleViolationCollection); 47 | static TMultiMap> AllRuleViolationsMappedByViolatedLintRuleShared(const TArray>& RuleViolationCollection); 48 | 49 | bool PopulateAssetData(); 50 | 51 | UPROPERTY(EditAnywhere, Category = "Lint") 52 | TWeakObjectPtr Violator; 53 | 54 | UPROPERTY(EditAnywhere, Category = "Lint") 55 | TSubclassOf ViolatedRule; 56 | 57 | UPROPERTY(EditAnywhere, Category = "Lint") 58 | FText RecommendedAction; 59 | 60 | FAssetData ViolatorAssetData; 61 | }; 62 | 63 | /** 64 | *Comment 65 | */ 66 | UCLASS(BlueprintType, Blueprintable, Abstract) 67 | class LINTER_API ULintRule : public UObject 68 | { 69 | GENERATED_BODY() 70 | 71 | public: 72 | 73 | ULintRule(const FObjectInitializer& ObjectInitializer); 74 | 75 | UPROPERTY(EditDefaultsOnly, Category = "Display") 76 | FName RuleGroup; 77 | 78 | UPROPERTY(EditDefaultsOnly, Category = "Display") 79 | FText RuleTitle; 80 | 81 | UPROPERTY(EditDefaultsOnly, Category = "Display") 82 | FText RuleDescription; 83 | 84 | UPROPERTY(EditDefaultsOnly, Category = "Display") 85 | FString RuleURL; 86 | 87 | UPROPERTY(EditDefaultsOnly, Category = "Display") 88 | ELintRuleSeverity RuleSeverity; 89 | 90 | UPROPERTY(EditDefaultsOnly, Category = "Settings", AdvancedDisplay) 91 | bool bRequiresGameThread = false; 92 | 93 | UFUNCTION(BlueprintCallable, Category = "Lint") 94 | virtual bool PassesRule(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; 95 | 96 | UFUNCTION(BlueprintCallable, Category = "Display") 97 | virtual bool IsRuleSuppressed() const; 98 | 99 | UFUNCTION(BlueprintNativeEvent, Category = "Display") 100 | FName GetRuleBasedObjectVariantName(UObject* ObjectToLint) const; 101 | 102 | protected: 103 | 104 | /* This is the function that child lint rules should override to perform the meat of the rule check 105 | * You do not call this directly. Always call PassesRule. PassesRule forwards to the PassesRule_Internal ONLY IF 106 | * data is valid and the rule is not suppressed, therefore it is worth checking. 107 | */ 108 | UFUNCTION(BlueprintNativeEvent, Category = "Lint") 109 | bool PassesRule_Internal(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const; 110 | 111 | private: 112 | 113 | }; 114 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/LintReportAssetDetails.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "UI/LintReportAssetDetails.h" 3 | #include "LinterStyle.h" 4 | #include "Widgets/Layout/SBorder.h" 5 | #include "EditorStyleSet.h" 6 | #include "Widgets/SBoxPanel.h" 7 | #include "Widgets/Layout/SExpandableArea.h" 8 | #include "ContentBrowserModule.h" 9 | #include "IContentBrowserSingleton.h" 10 | #include "AssetRegistryModule.h" 11 | #include "Widgets/Input/SHyperlink.h" 12 | #include "Widgets/Layout/SSpacer.h" 13 | #include "IAssetTools.h" 14 | #include "AssetToolsModule.h" 15 | #include "Misc/MessageDialog.h" 16 | #include "Internationalization/Internationalization.h" 17 | #include "Widgets/Text/STextBlock.h" 18 | #include "Framework/Views/ITypedTableView.h" 19 | #include "UI/LintReportAssetError.h" 20 | #include "LintRule.h" 21 | #include "AssetThumbnail.h" 22 | 23 | 24 | #define LOCTEXT_NAMESPACE "LintReport" 25 | 26 | void SLintReportAssetDetails::Construct(const FArguments& Args) 27 | { 28 | AssetData = Args._AssetData; 29 | RuleViolations = Args._RuleViolations; 30 | ThumbnailPool = Args._ThumbnailPool; 31 | 32 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 33 | 34 | FText AssetName = FText::FromString(AssetData.Get().AssetName.ToString()); 35 | FText AssetPath = FText::FromString(AssetData.Get().GetFullName()); 36 | 37 | const TSharedPtr AssetThumbnail = MakeShareable(new FAssetThumbnail(AssetData.Get().GetAsset(), 96, 96, ThumbnailPool.Get())); 38 | 39 | int32 NumErrors = 0; 40 | int32 NumWarnings = 0; 41 | 42 | for (TSharedPtr RuleViolation : RuleViolations.Get()) 43 | { 44 | switch (RuleViolation->ViolatedRule.Get()->GetDefaultObject()->RuleSeverity) 45 | { 46 | case ELintRuleSeverity::Error: 47 | NumErrors++; 48 | break; 49 | case ELintRuleSeverity::Warning: 50 | NumWarnings++; 51 | break; 52 | case ELintRuleSeverity::Info: 53 | break; 54 | //case ELintRuleSeverity::Ignore: 55 | default: 56 | break; 57 | } 58 | } 59 | 60 | ChildSlot 61 | [ 62 | SNew(SBorder) 63 | .BorderImage(FEditorStyle::GetBrush("NoBorder")) 64 | .Padding(PaddingAmount) 65 | [ 66 | SNew(SBorder) 67 | .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) 68 | .Padding(PaddingAmount) 69 | [ 70 | SNew(SVerticalBox) 71 | + SVerticalBox::Slot() 72 | .AutoHeight() 73 | .Padding(PaddingAmount) 74 | [ 75 | SNew(SExpandableArea) 76 | .InitiallyCollapsed(false) 77 | .HeaderContent() 78 | [ 79 | SNew(SHorizontalBox) 80 | + SHorizontalBox::Slot() 81 | .AutoWidth() 82 | [ 83 | SNew(STextBlock) 84 | .Text(AssetName) 85 | .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") 86 | ] 87 | ] 88 | .BodyContent() 89 | [ 90 | SNew(SHorizontalBox) 91 | + SHorizontalBox::Slot() 92 | .AutoWidth() 93 | .VAlign(VAlign_Top) 94 | .Padding(PaddingAmount) 95 | [ 96 | SNew(SBox) 97 | .WidthOverride(96.0f) 98 | .HeightOverride(96.0f) 99 | [ 100 | AssetThumbnail->MakeThumbnailWidget() 101 | ] 102 | ] 103 | + SHorizontalBox::Slot() 104 | .Padding(PaddingAmount) 105 | [ 106 | SNew(SVerticalBox) 107 | + SVerticalBox::Slot() 108 | .AutoHeight() 109 | .HAlign(HAlign_Left) 110 | .Padding(PaddingAmount) 111 | [ 112 | SNew(SHyperlink) 113 | .Text(AssetPath) 114 | .OnNavigate_Lambda([&]() 115 | { 116 | FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); 117 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); 118 | TArray AssetDatas; 119 | AssetDatas.Push(AssetData.Get()); 120 | ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); 121 | }) 122 | ] 123 | + SVerticalBox::Slot() 124 | .AutoHeight() 125 | .Padding(PaddingAmount) 126 | [ 127 | SNew(STextBlock) 128 | .Visibility((RuleViolations.Get().Num() > 0) ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed) 129 | .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)) 130 | ] 131 | + SVerticalBox::Slot() 132 | .AutoHeight() 133 | .Padding(PaddingAmount) 134 | [ 135 | SNew(SLintReportAssetErrorList) 136 | .RuleViolations(RuleViolations) 137 | ] 138 | ] 139 | ] 140 | ] 141 | ] 142 | ] 143 | ]; 144 | } 145 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Public/TooltipEditor/TooltipTool.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Gamemakin LLC. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "EditorStyleSet.h" 6 | 7 | #include "Widgets/SCompoundWidget.h" 8 | #include "Widgets/SUserWidget.h" 9 | #include "Widgets/SWindow.h" 10 | #include "Widgets/Layout/SSeparator.h" 11 | #include "Widgets/Layout/SUniformGridPanel.h" 12 | #include "Widgets/Input/SEditableTextBox.h" 13 | #include "Widgets/Input/SCheckBox.h" 14 | #include "Widgets/Input/SComboBox.h" 15 | #include "Widgets/Input/SMultiLineEditableTextBox.h" 16 | #include "Widgets/Views/SListView.h" 17 | #include "AssetData.h" 18 | #include "Engine/Blueprint.h" 19 | #include "K2Node_FunctionEntry.h" 20 | #include "K2Node_FunctionResult.h" 21 | 22 | #include "TooltipStringHelper.h" 23 | 24 | #define LOCTEXT_NAMESPACE "LinterTooltipTool" 25 | 26 | 27 | /** 28 | * Helper struct for showing function tooltip widgets 29 | **/ 30 | struct FBPFunctionPointers 31 | { 32 | UK2Node_FunctionEntry* FunctionEntryNode; 33 | UK2Node_FunctionResult* FunctionResultNode; 34 | FName FunctionName; 35 | 36 | FBPFunctionPointers() 37 | { 38 | } 39 | 40 | FBPFunctionPointers(UK2Node_FunctionEntry* InFunctionEntryNode, UK2Node_FunctionResult* InFunctionResultNode, FName InFunctionName) 41 | : FunctionEntryNode(InFunctionEntryNode) 42 | , FunctionResultNode(InFunctionResultNode) 43 | , FunctionName(InFunctionName) 44 | { 45 | } 46 | }; 47 | 48 | /** 49 | * FTooltipTool 50 | * 51 | * Wrapper class for STooltipTool. This class creates and launches a dialog then awaits the 52 | * result to return to the user. 53 | */ 54 | class FTooltipTool 55 | { 56 | public: 57 | enum EResult 58 | { 59 | Cancel = 0, // No/Cancel, normal usage would stop the current action 60 | Confirm = 1, // Yes/Ok/Etc, normal usage would continue with action 61 | }; 62 | 63 | FTooltipTool(const TArray Assets); 64 | 65 | /** Shows the dialog box and waits for the user to respond. */ 66 | EResult ShowModal(); 67 | 68 | TArray BlueprintsInternal; 69 | TArray> Blueprints; 70 | 71 | private: 72 | 73 | /** Cached pointer to the modal window */ 74 | TSharedPtr DialogWindow; 75 | 76 | /** Cached pointer to the batch rename tool widget */ 77 | TSharedPtr DialogWidget; 78 | }; 79 | 80 | /** 81 | * Slate panel for batch renaming 82 | */ 83 | class STooltipTool : public SCompoundWidget 84 | { 85 | public: 86 | 87 | SLATE_BEGIN_ARGS(STooltipTool) 88 | {} 89 | /** Window in which this widget resides */ 90 | SLATE_ATTRIBUTE(TSharedPtr, ParentWindow) 91 | SLATE_ATTRIBUTE(TArray>, Blueprints) 92 | SLATE_END_ARGS() 93 | 94 | /** 95 | * Constructs this widget 96 | * 97 | * @param InArgs The declaration data for this widget 98 | */ 99 | void Construct(const FArguments& InArgs); 100 | 101 | /** 102 | * Returns the EResult of the button which the user pressed. Closing of the dialog 103 | * in any other way than clicking "Ok" results in this returning a "Cancel" value 104 | */ 105 | FTooltipTool::EResult GetUserResponse() const; 106 | 107 | private: 108 | 109 | /** 110 | * Handles when a button is pressed, should be bound with appropriate EResult Key 111 | * 112 | * @param ButtonID - The return type of the button which has been pressed. 113 | */ 114 | FReply OnButtonClick(FTooltipTool::EResult ButtonID); 115 | 116 | FText GetSelectedBlueprintText() const; 117 | 118 | void UpdateVariableTooltipText(const FText& NewText); 119 | void UpdateCurrentFunctionTooltipText(); 120 | 121 | void RebuildMemberList(); 122 | 123 | /** Stores the users response to this dialog */ 124 | FTooltipTool::EResult UserResponse; 125 | 126 | /** Pointer to the window which holds this Widget, required for modal control */ 127 | TSharedPtr ParentWindow; 128 | 129 | public: 130 | 131 | 132 | 133 | TAttribute>> Blueprints; 134 | TSharedPtr>> BlueprintComboBox; 135 | 136 | FText CurrentFunctionDescription; 137 | 138 | TArray> FunctionPointers; 139 | TSharedPtr>> FunctionListView; 140 | TSharedPtr FunctionDescriptionTooltipBox; 141 | TArray> FunctionArgumentDescriptions; 142 | TSharedPtr>> FunctionArgumentListView; 143 | TArray> FunctionOutputDescriptions; 144 | TSharedPtr>> FunctionOutputListView; 145 | 146 | 147 | TArray> Members; 148 | TSharedPtr>> MemberListView; 149 | TSharedPtr VariableTooltipEditableTextBox; 150 | 151 | TSharedPtr CommitTextButton; 152 | TSharedPtr CommitOnTextChangeCheckBox; 153 | }; 154 | 155 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/Linter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "Linter.h" 4 | #include "ISettingsModule.h" 5 | #include "Framework/Docking/TabManager.h" 6 | #include "LevelEditor.h" 7 | #include "Widgets/Input/SButton.h" 8 | #include "Styling/SlateStyle.h" 9 | #include "AssetRegistryModule.h" 10 | #include "IAssetRegistry.h" 11 | #include "AssetData.h" 12 | #include "ContentBrowserModule.h" 13 | #include "PropertyEditorModule.h" 14 | 15 | #include "LinterStyle.h" 16 | #include "LinterContentBrowserExtensions.h" 17 | #include "LinterNamingConvention.h" 18 | #include "LinterSettings.h" 19 | #include "UI/LintWizard.h" 20 | #include "LintRuleSet.h" 21 | 22 | #define LOCTEXT_NAMESPACE "FLinterModule" 23 | 24 | static const FName LinterTabName = "LinterTab"; 25 | 26 | void FLinterModule::StartupModule() 27 | { 28 | // Load the asset registry module 29 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); 30 | IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); 31 | 32 | if (AssetRegistry.IsLoadingAssets()) 33 | { 34 | AssetRegistry.OnFilesLoaded().AddRaw(this, &FLinterModule::OnInitialAssetRegistrySearchComplete); 35 | } 36 | else 37 | { 38 | OnInitialAssetRegistrySearchComplete(); 39 | } 40 | 41 | // Integrate Linter actions into existing editor context menus 42 | if (!IsRunningCommandlet()) 43 | { 44 | // Register slate style overrides 45 | FLinterStyle::Initialize(); 46 | TSharedPtr StyleSetPtr = FLinterStyle::StyleSet; 47 | 48 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 49 | { 50 | SettingsModule->RegisterSettings("Project", "Plugins", "Linter", 51 | LOCTEXT("RuntimeSettingsName", "Linter"), 52 | LOCTEXT("RuntimeSettingsDescription", "Configure the Linter plugin"), 53 | GetMutableDefault()); 54 | } 55 | 56 | // Install UI Hooks 57 | FLinterContentBrowserExtensions::InstallHooks(this, &ContentBrowserExtenderDelegateHandle, &AssetExtenderDelegateHandle); 58 | 59 | //Register our UI 60 | FGlobalTabmanager::Get()->RegisterNomadTabSpawner( 61 | LinterTabName, 62 | FOnSpawnTab::CreateStatic(&FLinterModule::SpawnTab, StyleSetPtr)) 63 | .SetDisplayName(LOCTEXT("LinterTabName", "Linter")) 64 | .SetTooltipText(LOCTEXT("LinterTabToolTip", "Linter")) 65 | .SetMenuType(ETabSpawnerMenuType::Hidden); 66 | 67 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); 68 | PropertyModule.RegisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FLinterNamingConventionDetails::MakeInstance)); 69 | } 70 | } 71 | 72 | void FLinterModule::ShutdownModule() 73 | { 74 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 75 | { 76 | SettingsModule->UnregisterSettings("Project", "Plugins", "Linter"); 77 | } 78 | 79 | if (UObjectInitialized()) 80 | { 81 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); 82 | PropertyModule.UnregisterCustomClassLayout(ULinterNamingConvention::StaticClass()->GetFName()); 83 | 84 | FLinterContentBrowserExtensions::RemoveHooks(this, &ContentBrowserExtenderDelegateHandle, &AssetExtenderDelegateHandle); 85 | 86 | if (FModuleManager::Get().IsModuleLoaded(TEXT("LevelEditor"))) 87 | { 88 | FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); 89 | LevelEditorModule.OnTabManagerChanged().Remove(LevelEditorTabManagerChangedHandle); 90 | } 91 | 92 | FGlobalTabmanager::Get()->UnregisterTabSpawner(LinterTabName); 93 | 94 | // Unregister slate style overrides 95 | FLinterStyle::Shutdown(); 96 | } 97 | } 98 | 99 | 100 | TSharedRef FLinterModule::SpawnTab(const FSpawnTabArgs& TabSpawnArgs, TSharedPtr StyleSet) 101 | { 102 | const FSlateBrush* IconBrush = StyleSet->GetBrush("Linter.Toolbar.Icon"); 103 | 104 | const TSharedRef MajorTab = 105 | SNew(SDockTab) 106 | .Icon(IconBrush) 107 | .TabRole(ETabRole::MajorTab); 108 | 109 | MajorTab->SetContent(SNew(SLintWizard)); 110 | 111 | return MajorTab; 112 | } 113 | 114 | void FLinterModule::OnInitialAssetRegistrySearchComplete() 115 | { 116 | TryToLoadAllLintRuleSets(); 117 | } 118 | 119 | void FLinterModule::TryToLoadAllLintRuleSets() 120 | { 121 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(FName("AssetRegistry")); 122 | IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); 123 | 124 | TArray FoundRuleSets; 125 | AssetRegistry.GetAssetsByClass(ULintRuleSet::StaticClass()->GetFName(), FoundRuleSets, true); 126 | 127 | // Attempt to get all RuleSets in memory so that linting tools are better aware of them 128 | for (const FAssetData& RuleSetData : FoundRuleSets) 129 | { 130 | if (!RuleSetData.IsAssetLoaded()) 131 | { 132 | RuleSetData.GetAsset(); 133 | } 134 | } 135 | } 136 | 137 | #undef LOCTEXT_NAMESPACE 138 | 139 | IMPLEMENT_MODULE(FLinterModule, Linter) 140 | DEFINE_LOG_CATEGORY(LogLinter); -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/SStepWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | #include "UI/SStepWidget.h" 3 | #include "Widgets/SBoxPanel.h" 4 | #include "Widgets/Layout/SBorder.h" 5 | #include "Widgets/Layout/SBox.h" 6 | #include "Widgets/Images/SImage.h" 7 | #include "Widgets/Input/SButton.h" 8 | #include "Widgets/Text/STextBlock.h" 9 | #include "Widgets/Images/SThrobber.h" 10 | #include "Widgets/Text/SRichTextBlock.h" 11 | 12 | bool SStepWidget::IsStepCompleted(bool bAllowWarning /*= true*/) 13 | { 14 | EStepStatus Status = StepStatus.Get(); 15 | if (bAllowWarning && Status == EStepStatus::Warning) 16 | { 17 | return true; 18 | } 19 | 20 | return Status == EStepStatus::Success; 21 | } 22 | 23 | void SStepWidget::Construct(const FArguments& Args) 24 | { 25 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 26 | 27 | StepStatus = Args._StepStatus; 28 | OnPerformAction = Args._OnPerformAction; 29 | StepActionText = Args._StepActionText; 30 | ShowStepStatusIcon = Args._ShowStepStatusIcon; 31 | 32 | // Visibility lambda based on whether step is in progress 33 | auto VisibleIfInProgress = [this]() 34 | { 35 | return StepStatus.Get(EStepStatus::NoStatus) == EStepStatus::InProgress ? EVisibility::Visible : EVisibility::Collapsed; 36 | }; 37 | 38 | // Enabled lambda based on whether this widget has a step status that requires action 39 | auto EnabledBasedOnStepStatus = [this]() -> bool 40 | { 41 | switch (StepStatus.Get(EStepStatus::NoStatus)) 42 | { 43 | case EStepStatus::NoStatus: 44 | case EStepStatus::InProgress: 45 | case EStepStatus::Success: 46 | return false; 47 | case EStepStatus::Unknown: 48 | case EStepStatus::Warning: 49 | case EStepStatus::Error: 50 | case EStepStatus::NeedsUpdate: 51 | return true; 52 | } 53 | return false; 54 | }; 55 | 56 | ChildSlot 57 | [ 58 | SNew(SBorder) 59 | .BorderImage(FEditorStyle::GetBrush("NoBorder")) 60 | .Padding(PaddingAmount) 61 | [ 62 | SNew(SBorder) 63 | .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) 64 | .Padding(PaddingAmount) 65 | [ 66 | SNew(SHorizontalBox) 67 | // Status Image 68 | + SHorizontalBox::Slot() 69 | .Padding(PaddingAmount) 70 | .AutoWidth() 71 | [ 72 | SNew(SImage) 73 | .Visibility_Lambda([&]() { return StepStatus.Get(EStepStatus::NoStatus) == EStepStatus::NoStatus || !ShowStepStatusIcon.Get(true) ? EVisibility::Collapsed : EVisibility::Visible; }) 74 | .Image_Lambda([&]() 75 | { 76 | switch (StepStatus.Get(EStepStatus::NoStatus)) 77 | { 78 | case NoStatus: 79 | case Unknown: 80 | return FLinterStyle::Get()->GetBrush("Linter.Step.Unknown"); 81 | case InProgress: 82 | case NeedsUpdate: 83 | return FLinterStyle::Get()->GetBrush("Linter.Step.Working"); 84 | case Warning: 85 | return FLinterStyle::Get()->GetBrush("Linter.Step.Warning"); 86 | case Error: 87 | return FLinterStyle::Get()->GetBrush("Linter.Step.Error"); 88 | case Success: 89 | return FLinterStyle::Get()->GetBrush("Linter.Step.Good"); 90 | } 91 | 92 | return FLinterStyle::Get()->GetBrush("Linter.Step.Unknown"); 93 | }) 94 | ] 95 | // Template thumbnail image 96 | + SHorizontalBox::Slot() 97 | .Padding(4.0) 98 | .AutoWidth() 99 | .VAlign(VAlign_Top) 100 | [ 101 | SNew(SImage) 102 | .Visibility(Args._Icon.IsSet() ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed) 103 | .Image(Args._Icon) 104 | ] 105 | // Template name and description 106 | + SHorizontalBox::Slot() 107 | [ 108 | SNew(SVerticalBox) 109 | 110 | + SVerticalBox::Slot() 111 | .AutoHeight() 112 | .Padding(PaddingAmount) 113 | [ 114 | SNew(STextBlock) 115 | .Text(Args._StepName) 116 | .TextStyle(FLinterStyle::Get(), "Linter.Report.RuleTitle") 117 | ] 118 | 119 | + SVerticalBox::Slot() 120 | .AutoHeight() 121 | .Padding(PaddingAmount) 122 | [ 123 | SNew(SRichTextBlock) 124 | .Text(Args._StepDesc) 125 | .TextStyle(FLinterStyle::Get(), "Linter.Report.DescriptionText") 126 | .AutoWrapText(true) 127 | ] 128 | + SVerticalBox::Slot() 129 | .AutoHeight() 130 | .Padding(PaddingAmount) 131 | [ 132 | SNew(SHorizontalBox) 133 | + SHorizontalBox::Slot() 134 | .AutoWidth() 135 | [ 136 | SNew(SButton) 137 | .IsEnabled_Lambda(EnabledBasedOnStepStatus) 138 | .Visibility_Lambda([&]() { return StepStatus.Get(EStepStatus::NoStatus) == EStepStatus::NoStatus ? EVisibility::Collapsed : EVisibility::Visible; }) 139 | .OnClicked_Lambda([&]() 140 | { 141 | FScopedSlowTask SlowTask(1.0f, StepActionText.Get(FText())); 142 | SlowTask.MakeDialog(); 143 | 144 | OnPerformAction.ExecuteIfBound(SlowTask); 145 | return FReply::Handled(); 146 | }) 147 | [ 148 | SNew(STextBlock) 149 | .Text(StepActionText) 150 | ] 151 | ] 152 | + SHorizontalBox::Slot() 153 | .AutoWidth() 154 | [ 155 | SNew(SThrobber) 156 | .Visibility_Lambda(VisibleIfInProgress) 157 | ] 158 | ] 159 | ] 160 | ] 161 | ] 162 | ]; 163 | } 164 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LinterStyle.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "LinterStyle.h" 4 | #include "Styling/SlateTypes.h" 5 | #include "Styling/SlateStyle.h" 6 | #include "Interfaces/IPluginManager.h" 7 | #include "Styling/SlateStyleRegistry.h" 8 | #include "EditorStyleSet.h" 9 | 10 | #define IMAGE_PLUGIN_BRUSH( RelativePath, ... ) FSlateImageBrush( FLinterStyle::InContent( RelativePath, ".png" ), __VA_ARGS__ ) 11 | #define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) 12 | #define BOX_BRUSH(RelativePath, ...) FSlateBoxBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) 13 | #define TTF_FONT(RelativePath, ...) FSlateFontInfo(StyleSet->RootToContentDir(RelativePath, TEXT(".ttf")), __VA_ARGS__) 14 | #define TTF_CORE_FONT(RelativePath, ...) FSlateFontInfo(StyleSet->RootToCoreContentDir(RelativePath, TEXT(".ttf") ), __VA_ARGS__) 15 | 16 | FString FLinterStyle::InContent(const FString& RelativePath, const ANSICHAR* Extension) 17 | { 18 | static FString ContentDir = IPluginManager::Get().FindPlugin(TEXT("Linter"))->GetContentDir(); 19 | return (ContentDir / RelativePath) + Extension; 20 | } 21 | 22 | TSharedPtr< FSlateStyleSet > FLinterStyle::StyleSet = nullptr; 23 | TSharedPtr< class ISlateStyle > FLinterStyle::Get() { return StyleSet; } 24 | 25 | FName FLinterStyle::GetStyleSetName() 26 | { 27 | static FName LinterStyleName(TEXT("LinterStyle")); 28 | return LinterStyleName; 29 | } 30 | 31 | void FLinterStyle::Initialize() 32 | { 33 | // Const icon sizes 34 | const FVector2D Icon8x8(8.0f, 8.0f); 35 | const FVector2D Icon14x14(14.0f, 14.0f); 36 | const FVector2D Icon16x16(16.0f, 16.0f); 37 | const FVector2D Icon20x20(20.0f, 20.0f); 38 | const FVector2D Icon40x40(40.0f, 40.0f); 39 | const FVector2D Icon64x64(64.0f, 64.0f); 40 | const FVector2D Icon128x128(128.0f, 128.0f); 41 | 42 | // Only register once 43 | if (StyleSet.IsValid()) 44 | { 45 | return; 46 | } 47 | 48 | StyleSet = MakeShareable(new FSlateStyleSet(GetStyleSetName())); 49 | StyleSet->SetContentRoot(FPaths::EngineContentDir() / TEXT("Editor/Slate")); 50 | StyleSet->SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); 51 | 52 | // Asset actions 53 | { 54 | StyleSet->Set("AssetActions.RunLinter", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); 55 | StyleSet->Set("AssetActions.BatchRename", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); 56 | StyleSet->Set("AssetActions.TooltipTool", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); 57 | 58 | // Toolbar Button Icons 59 | StyleSet->Set("Linter.Toolbar.Icon", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Texture_Run_16x"), Icon16x16)); 60 | 61 | // Report Images 62 | StyleSet->Set("Linter.Step.Unknown", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Unknown_64px", Icon64x64)); 63 | StyleSet->Set("Linter.Step.Error", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Fail_64px", Icon64x64)); 64 | StyleSet->Set("Linter.Step.Good", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Good_64px", Icon64x64)); 65 | StyleSet->Set("Linter.Step.Working", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Working_64px", Icon64x64)); 66 | StyleSet->Set("Linter.Step.Warning", new IMAGE_PLUGIN_BRUSH("Icons/CompileStatus_Warning_64px", Icon64x64)); 67 | 68 | StyleSet->Set("Linter.Report.Link", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_Help_Documentation_16x"), Icon16x16)); 69 | StyleSet->Set("Linter.Report.Warning", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Warning_14x"), Icon14x14)); 70 | StyleSet->Set("Linter.Report.Error", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Error_14x"), Icon14x14)); 71 | 72 | StyleSet->Set("Linter.Report.Info", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MessageLog_Info_14x"), Icon20x20)); 73 | 74 | 75 | StyleSet->Set("Linter.Step.BuildLighting.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_MapCheck_64x"), Icon64x64)); 76 | StyleSet->Set("Linter.Step.Package.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_AddContent_64x"), Icon64x64)); 77 | StyleSet->Set("Linter.Step.SaveAll.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_file_saveall_64x"), Icon64x64)); 78 | StyleSet->Set("Linter.Step.FixupRedirects.Thumbnail", new IMAGE_PLUGIN_BRUSH(TEXT("Icons/icon_tab_Toolbars_64x"), Icon64x64)); 79 | 80 | // PaCK Sizing 81 | StyleSet->Set("Linter.Padding", 2.0f); 82 | 83 | // PaCK Fonts 84 | const FTextBlockStyle NormalText = FEditorStyle::GetWidgetStyle("NormalText"); 85 | 86 | FTextBlockStyle NameText = FTextBlockStyle(NormalText) 87 | .SetColorAndOpacity(FLinearColor(0.9f, 0.9f, 0.9f)); 88 | { 89 | NameText.Font.Size = 14; 90 | StyleSet->Set("Linter.Report.AssetName", NameText); 91 | } 92 | 93 | FTextBlockStyle RuleTitleText = FTextBlockStyle(NormalText) 94 | .SetColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f)); 95 | { 96 | RuleTitleText.Font.Size = 12; 97 | StyleSet->Set("Linter.Report.RuleTitle", RuleTitleText); 98 | } 99 | 100 | FTextBlockStyle DescriptionText = FTextBlockStyle(NormalText) 101 | .SetColorAndOpacity(FLinearColor(0.8f, 0.8f, 0.8f)); 102 | { 103 | DescriptionText.Font.Size = 10; 104 | StyleSet->Set("Linter.Report.DescriptionText", DescriptionText); 105 | } 106 | } 107 | 108 | FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); 109 | }; 110 | 111 | #undef IMAGE_PLUGIN_BRUSH 112 | #undef IMAGE_BRUSH 113 | #undef BOX_BRUSH 114 | #undef TTF_FONT 115 | #undef TTF_CORE_FONT 116 | 117 | void FLinterStyle::Shutdown() 118 | { 119 | if (StyleSet.IsValid()) 120 | { 121 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); 122 | ensure(StyleSet.IsUnique()); 123 | StyleSet.Reset(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/LintRuleSet.cpp: -------------------------------------------------------------------------------- 1 | #include "LintRuleSet.h" 2 | #include "LintRunner.h" 3 | 4 | #include "AssetRegistryModule.h" 5 | #include "Modules/ModuleManager.h" 6 | #include "HAL/RunnableThread.h" 7 | 8 | ULintRuleSet::ULintRuleSet(const FObjectInitializer& ObjectInitializer) 9 | : Super(ObjectInitializer) 10 | { 11 | 12 | } 13 | 14 | ULinterNamingConvention* ULintRuleSet::GetNamingConvention() const 15 | { 16 | return NamingConvention.Get(); 17 | } 18 | 19 | TArray ULintRuleSet::LintPath(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask /*= nullptr*/) const 20 | { 21 | NamingConvention.LoadSynchronous(); 22 | 23 | TArray RuleViolations; 24 | 25 | if (AssetPaths.Num() == 0) 26 | { 27 | AssetPaths.Push(TEXT("/Game")); 28 | } 29 | 30 | // Begin loading assets 31 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); 32 | 33 | UE_LOG(LogLinter, Display, TEXT("Loading the asset registry...")); 34 | AssetRegistryModule.Get().SearchAllAssets(/*bSynchronousSearch =*/true); 35 | UE_LOG(LogLinter, Display, TEXT("Finished loading the asset registry. Loading assets...")); 36 | 37 | TArray AssetList; 38 | 39 | FARFilter ARFilter; 40 | ARFilter.bRecursivePaths = true; 41 | 42 | for (const FString& AssetPath : AssetPaths) 43 | { 44 | UE_LOG(LogLinter, Display, TEXT("Adding path \"%s\" to be linted."), *AssetPath); 45 | ARFilter.PackagePaths.Push(FName(*AssetPath)); 46 | } 47 | 48 | AssetRegistryModule.Get().GetAssets(ARFilter, AssetList); 49 | 50 | TArray LintRunners; 51 | TArray Threads; 52 | 53 | if (ParentScopedSlowTask != nullptr) 54 | { 55 | ParentScopedSlowTask->TotalAmountOfWork = AssetList.Num() + 2; 56 | ParentScopedSlowTask->CompletedWork = 0.0f; 57 | } 58 | 59 | for (FAssetData const& Asset : AssetList) 60 | { 61 | check(Asset.IsValid()); 62 | UE_LOG(LogLinter, Verbose, TEXT("Creating Lint Thread for asset \"%s\"."), *Asset.AssetName.ToString()); 63 | UObject* Object = Asset.GetAsset(); 64 | check(Object != nullptr); 65 | 66 | FLintRunner* Runner = new FLintRunner(Object, this, &RuleViolations, ParentScopedSlowTask); 67 | check(Runner != nullptr); 68 | 69 | LintRunners.Add(Runner); 70 | 71 | if (Runner->RequiresGamethread()) 72 | { 73 | Runner->Run(); 74 | // If we're given a scoped slow task, update its progress now... 75 | if (ParentScopedSlowTask != nullptr) 76 | { 77 | ParentScopedSlowTask->EnterProgressFrame(1.0f); 78 | } 79 | } 80 | else 81 | { 82 | Threads.Push(FRunnableThread::Create(Runner, *FString::Printf(TEXT("FLintRunner - %s"), *Asset.ObjectPath.ToString()), 0, TPri_Normal)); 83 | if (ParentScopedSlowTask != nullptr) 84 | { 85 | ParentScopedSlowTask->EnterProgressFrame(1.0f); 86 | } 87 | } 88 | } 89 | 90 | for (FRunnableThread* Thread : Threads) 91 | { 92 | Thread->WaitForCompletion(); 93 | } 94 | 95 | if (ParentScopedSlowTask != nullptr) 96 | { 97 | ParentScopedSlowTask->EnterProgressFrame(1.0f, NSLOCTEXT("Linter", "ScanTaskFinished", "Tabulating Data...")); 98 | } 99 | 100 | return RuleViolations; 101 | } 102 | 103 | TArray> ULintRuleSet::LintPathShared(TArray AssetPaths, FScopedSlowTask* ParentScopedSlowTask /*= nullptr*/) const 104 | { 105 | TArray RuleViolations = LintPath(AssetPaths, ParentScopedSlowTask); 106 | 107 | TArray> SharedRuleViolations; 108 | for (FLintRuleViolation Violation : RuleViolations) 109 | { 110 | TSharedPtr SharedViolation = TSharedPtr(new FLintRuleViolation(Violation)); 111 | SharedViolation->PopulateAssetData(); 112 | SharedRuleViolations.Push(SharedViolation); 113 | } 114 | 115 | return SharedRuleViolations; 116 | } 117 | 118 | const FLintRuleList* ULintRuleSet::GetLintRuleListForClass(TSoftClassPtr Class) const 119 | { 120 | UClass* searchClass = Class.LoadSynchronous(); 121 | while (searchClass != nullptr) 122 | { 123 | const FLintRuleList* pRuleList = ClassLintRulesMap.Find(searchClass); 124 | if (pRuleList != nullptr) 125 | { 126 | return pRuleList; 127 | } 128 | 129 | // @HACK: If we reach UObject, find our hack rule for fallback 130 | if (searchClass == UObject::StaticClass()) 131 | { 132 | const FLintRuleList* anyObjectRuleList = ClassLintRulesMap.Find(UAnyObject_LinterDummyClass::StaticClass()); 133 | return anyObjectRuleList; 134 | } 135 | 136 | // Load our parent class in case we failed to get naming conventions 137 | searchClass = searchClass->GetSuperClass(); 138 | } 139 | 140 | return nullptr; 141 | } 142 | 143 | bool FLintRuleList::RequiresGameThread() const 144 | { 145 | for (TSubclassOf LintRuleSubClass : LintRules) 146 | { 147 | UClass* LintClass = LintRuleSubClass.Get(); 148 | if (LintClass != nullptr) 149 | { 150 | const ULintRule* LintRule = GetDefault(LintClass); 151 | if (LintRule != nullptr && LintRule->bRequiresGameThread) 152 | { 153 | return true; 154 | } 155 | } 156 | } 157 | 158 | return false; 159 | } 160 | 161 | bool FLintRuleList::PassesRules(UObject* ObjectToLint, const ULintRuleSet* ParentRuleSet, TArray& OutRuleViolations) const 162 | { 163 | OutRuleViolations.Empty(); 164 | 165 | bool bFailedAnyRule = false; 166 | for (TSubclassOf LintRuleSubClass : LintRules) 167 | { 168 | UClass* LintClass = LintRuleSubClass.Get(); 169 | if (LintClass != nullptr) 170 | { 171 | const ULintRule* LintRule = GetDefault(LintClass); 172 | if (LintRule != nullptr) 173 | { 174 | TArray ViolatedRules; 175 | bFailedAnyRule = !LintRule->PassesRule(ObjectToLint, ParentRuleSet, ViolatedRules) || bFailedAnyRule; 176 | OutRuleViolations.Append(ViolatedRules); 177 | } 178 | } 179 | } 180 | 181 | return !bFailedAnyRule; 182 | } 183 | -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/TooltipTool/TooltipStringHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "TooltipEditor/TooltipStringHelper.h" 2 | 3 | FText FTooltipStringHelper::ParseFunctionRawTooltipGetDescription(FString RawTooltip, bool bRemoveNewlines/* = false*/) 4 | { 5 | if (RawTooltip.Len() == 0) 6 | { 7 | return FText::GetEmpty(); 8 | } 9 | 10 | TArray Lines; 11 | RawTooltip.ParseIntoArrayLines(Lines); 12 | 13 | FString Description; 14 | 15 | for (FString Line : Lines) 16 | { 17 | if (Line.StartsWith(TEXT("@param"))) 18 | { 19 | break; 20 | } 21 | else if (Line.StartsWith(TEXT("@return"))) //@return is assumed to always be last 22 | { 23 | break; 24 | } 25 | else 26 | { 27 | if (Description.Len() > 0) 28 | { 29 | Description.Append(TEXT("\n")); 30 | } 31 | Description.Append(Line); 32 | } 33 | } 34 | 35 | if (bRemoveNewlines) 36 | { 37 | Description.ReplaceInline(TEXT("\r"), TEXT("")); 38 | Description.ReplaceInline(TEXT("\n"), TEXT(" ")); 39 | } 40 | 41 | return FText::FromString(Description); 42 | } 43 | 44 | bool FTooltipStringHelper::ParseFunctionRawTooltip(FString RawTooltip, FText& OutFunctionDescription, TArray>& Inputs, TArray>& Outputs, FText& OutReturnText) 45 | { 46 | if (RawTooltip.Len() == 0) 47 | { 48 | return false; 49 | } 50 | 51 | TArray Lines; 52 | RawTooltip.ParseIntoArrayLines(Lines); 53 | 54 | FString Description; 55 | bool bParsingDescription = true; 56 | 57 | FText CurrentArgumentName; 58 | FString CurrentArgumentTooltip; 59 | bool bCurrentArgumentIsInput = true; 60 | 61 | for (FString Line : Lines) 62 | { 63 | if (Line.StartsWith(TEXT("@param"))) 64 | { 65 | if (!bParsingDescription) 66 | { 67 | if (bCurrentArgumentIsInput) 68 | { 69 | FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); 70 | } 71 | else 72 | { 73 | FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); 74 | } 75 | 76 | CurrentArgumentName = FText::GetEmpty(); 77 | CurrentArgumentTooltip.Empty(); 78 | } 79 | 80 | bParsingDescription = false; 81 | bCurrentArgumentIsInput = true; 82 | 83 | Line = Line.RightChop(6); 84 | Line.TrimStartAndEndInline(); 85 | 86 | if (Line.StartsWith("[out]")) 87 | { 88 | bCurrentArgumentIsInput = false; 89 | Line = Line.RightChop(5); 90 | Line.TrimStartAndEndInline(); 91 | } 92 | 93 | Line = Line.ConvertTabsToSpaces(4); 94 | FString ArgumentName; 95 | FString ArgumentTooltip; 96 | if (Line.Split(TEXT(" "), &ArgumentName, &CurrentArgumentTooltip)) 97 | { 98 | CurrentArgumentName = FText::FromString(ArgumentName); 99 | CurrentArgumentTooltip.TrimStartAndEndInline(); 100 | } 101 | } 102 | else if (Line.StartsWith(TEXT("@return"))) //@return is assumed to always be last 103 | { 104 | if (!bParsingDescription) 105 | { 106 | if (bCurrentArgumentIsInput) 107 | { 108 | FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); 109 | } 110 | else 111 | { 112 | FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); 113 | } 114 | 115 | CurrentArgumentName = FText::GetEmpty(); 116 | CurrentArgumentTooltip.Empty(); 117 | } 118 | 119 | Line = Line.RightChop(7); 120 | Line.TrimStartAndEndInline(); 121 | OutReturnText = FText::FromString(Line); 122 | } 123 | else 124 | { 125 | if (bParsingDescription) 126 | { 127 | if (Description.Len() > 0) 128 | { 129 | Description.Append(TEXT("\n")); 130 | } 131 | Description.Append(Line); 132 | } 133 | else 134 | { 135 | Line.TrimStartAndEndInline(); 136 | CurrentArgumentTooltip.AppendChar(TEXT(' ')); 137 | CurrentArgumentTooltip.Append(Line); 138 | } 139 | } 140 | } 141 | 142 | if (!CurrentArgumentName.IsEmpty()) 143 | { 144 | if (!bParsingDescription) 145 | { 146 | if (bCurrentArgumentIsInput) 147 | { 148 | FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Inputs); 149 | } 150 | else 151 | { 152 | FTooltipStringHelper::FindAndUpdateArgumentTooltip(CurrentArgumentName, FText::FromString(CurrentArgumentTooltip), Outputs); 153 | } 154 | 155 | CurrentArgumentName = FText::GetEmpty(); 156 | CurrentArgumentTooltip.Empty(); 157 | } 158 | } 159 | 160 | OutFunctionDescription = FText::FromString(Description); 161 | return true; 162 | } 163 | 164 | FString FTooltipStringHelper::ConvertTooltipDataToRawTooltip(FText FunctionDescription, TArray> Inputs, TArray> Outputs) 165 | { 166 | FString RawTooltip = FunctionDescription.ToString(); 167 | for (TSharedPtr Arg : Inputs) 168 | { 169 | RawTooltip.Append(FString::Printf(TEXT("\n@param %s %s\t\t\t%s"), TEXT(" "), *Arg->ArgumentName.ToString(), *Arg->Tooltip.ToString())); 170 | } 171 | 172 | if (Outputs.Num() == 1) 173 | { 174 | RawTooltip.Append(FString::Printf(TEXT("\n@return %s"), *Outputs[0]->Tooltip.ToString())); 175 | } 176 | else 177 | { 178 | for (TSharedPtr Arg : Outputs) 179 | { 180 | RawTooltip.Append(FString::Printf(TEXT("\n@param %s %s\t\t\t%s"), TEXT("[out]"), *Arg->ArgumentName.ToString(), *Arg->Tooltip.ToString())); 181 | } 182 | } 183 | 184 | return RawTooltip; 185 | } 186 | 187 | bool FTooltipStringHelper::FindAndUpdateArgumentTooltip(FText ArgumentName, FText Tooltip, TArray>& Arguments) 188 | { 189 | TSharedPtr* FuncArg = Arguments.FindByPredicate([&](TSharedPtr Arg){ return Arg->ArgumentName.EqualTo(ArgumentName, ETextComparisonLevel::Quinary);}); 190 | if (FuncArg != nullptr) 191 | { 192 | (*FuncArg)->Tooltip = Tooltip; 193 | return true; 194 | } 195 | else 196 | { 197 | return false; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /docs/unrealguidelines.md: -------------------------------------------------------------------------------- 1 | # Unreal Engine Marketplace Guidelines 2 | 3 | This page covers how Linter implements rule sets based off of the [Unreal Engine Marketplace Guidelines](https://www.unrealengine.com/marketplace-guidelines). Not every guideline is currently supported but it is the goal of Linter to continually improve support for the Unreal Engine Marketplace Guidelines as well as other commonly used style guides. 4 | 5 | If you would like to contribute by adding support for additional guidelines, please join the discussion in the [Gamemakin LLC Community Discord](http://discord.gamemak.in). 6 | 7 | ## Disclaimer 8 | 9 | You are not guaranteed to have your product submission accepted by the Unreal Engine Marketplace simply because you might pass all these rules, however failing these rules will make your submission incredibly more likely to be rejected. 10 | 11 | ## 2.3.6 Particle Effects 12 | 13 | ### 2.3.6.b [Particle emitter names must be accurate and relevant and must not be "Particle Emitter" unless they're the only emitter in a given particle system](https://www.unrealengine.com/en-US/marketplace-guidelines#236b) 14 | 15 | Linter's interpretation of this is to simply check to see if a `UParticleSystem` asset has 2 or more emitters, and if it does, check to see if any of the emitters are named "Particle Emitter". If they are, then this rule is being violated. 16 | 17 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Particles_EmitterNames.MPLR_Particles_EmitterNames'`. 18 | 19 | ## 2.3.7 Textures 20 | 21 | ### 2.3.7.a [Both dimensions of each texture should have a size that is a power of 2 where applicable (e.g. 1024x512 or 1024x4096)](https://www.unrealengine.com/en-US/marketplace-guidelines#237a) 22 | 23 | Linter's interpretation is to do a simple "power of two" math test on both the width and the height of all textures. If a texture fails this power of two math test, it then is checked to see if it's `LODGROUP` is exempt from this rule. For the Unreal Engine Marketplace Guidelines, this rule set is configured to only allow textures of the `UI` `LODGROUP` to not have power of two sizes. 24 | 25 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Texture2D_PowerOfTwo.MPLR_Texture2D_PowerOfTwo'`. 26 | 27 | ### 2.3.7.b [Textures must have a maximum size in either dimension of 8192](https://www.unrealengine.com/en-US/marketplace-guidelines#237b) 28 | 29 | Linter's interpretation of this is to make sure a texture's width or height isn't bigger than 8192. That simple. 30 | 31 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Texture2D_Size_NotTooBig.MPLR_Texture2D_Size_NotTooBig'`. 32 | 33 | ## 2.4 Audio 34 | 35 | ### 2.4.c [Audio files must have a sample rate of 22050 Hz or 44100 Hz with no audio defects](https://www.unrealengine.com/en-US/marketplace-guidelines#24c) 36 | 37 | Linter's interpretation of this is pretty straight forward. `USoundWave` assets must have sample rates of either 22050 or 44100. 38 | 39 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_SoundWave_SampleRate.MPLR_SoundWave_SampleRate'`. 40 | 41 | ## 2.5 Blueprints 42 | 43 | ### 2.5.d [Blueprints must have no loose nodes unless they’re commented for example/tutorial purposes](https://www.unrealengine.com/en-US/marketplace-guidelines#25d) 44 | 45 | Linter's interpretation of this is that a `UBlueprint` that has any node on any of its graphs that has zero connections to any other node. 46 | 47 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Blueprint_LooseNodes.MPLR_Blueprint_LooseNodes'`. 48 | 49 | ### 2.5.e [Blueprints must generate no errors or consequential warnings](https://www.unrealengine.com/en-US/marketplace-guidelines#25e) 50 | 51 | Linter's interpretation of this is that a `UBlueprint` must not have a compile status of `BS_Error` or `BS_UpToDateWithWarnings`. 52 | 53 | That is, the compile button for this Blueprint should not have any error or warning icon on it. 54 | 55 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Blueprint_Compiles.MPLR_Blueprint_Compiles'`. 56 | 57 | ## 2.7 File Structure 58 | 59 | ### 2.7.1.a [Folder and files must be accurate and consistent in naming convention within the context of their project](https://www.unrealengine.com/en-US/marketplace-guidelines#271a) 60 | 61 | Linter's interpretation of this is that all assets should match a pattern defined in the rule set's `NamingConvention` asset. 62 | 63 | The Marketplace `NamingConvention` asset is `MarketplaceNamingConvention'/Linter/MarketplaceLinter/MarketplaceNamingConvention.MarketplaceNamingConvention'` and the rule that checks for this is `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_IsNamedCorrectly.MPLR_IsNamedCorrectly'`. 64 | 65 | ### 2.7.1.b [Folders and files must not be vaguely-named such as \"Assets\", \"NewFolder\", etc.](https://www.unrealengine.com/en-US/marketplace-guidelines#271b) 66 | 67 | Linter's interpretation of this is that all path elements must never be "Assets" or "NewFolder". 68 | 69 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Path_DisallowedPathNames.MPLR_Path_DisallowedPathNames'`. 70 | 71 | ### 2.7.1.c [Folder and file names must contain only English alphanumeric characters and underscores](https://www.unrealengine.com/en-US/marketplace-guidelines#271c) 72 | 73 | Linter's interpretation of this is that all path elements are tested against a Regular Expression `[^a-zA-Z0-9_]` which is only valid if the entire path element does not have any character except the letters "a through z", "A through Z", "0 through 9", and the '_' character. 74 | 75 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Path_AlphaNumeric.MPLR_Path_AlphaNumeric'`. 76 | 77 | ### 2.7.2.b [In order to reduce migration conflicts after importing project files from the Epic Games Launcher, all project-specific assets must be stored in one top level folder; there must be no other folders or files directly under the Content folder](https://www.unrealengine.com/en-US/marketplace-guidelines#272b) 78 | 79 | Linter's interpretation of this is that the parent folder of any asset should not be the path `/Content/`. 80 | 81 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Path_NoTopLevelAssets.MPLR_Path_NoTopLevelAssets'`. 82 | 83 | ### 2.7.2.d [Including the name of the top level folder under the Content folder, all asset file paths must be 140 characters or less](https://www.unrealengine.com/en-US/marketplace-guidelines#272d) 84 | 85 | Linter's interpretation of this is that the path of any asset returned by `UObject::GetPathName()`, with the redundant asset name chopped off, should not be longer than 140 characters. 86 | 87 | This is handled by `Blueprint'/Linter/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong.MPLR_Path_IsNotTooLong'`. The path name that is used for this asset for example would be `/Linter/MarketplaceLinter/LintRules/MPLR_Path_IsNotTooLong'`. -------------------------------------------------------------------------------- /Plugins/Linter/Source/Linter/Private/UI/LintReportRuleDetails.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 Gamemakin LLC. All Rights Reserved. 2 | #include "UI/LintReportRuleDetails.h" 3 | #include "LinterStyle.h" 4 | #include "Widgets/Layout/SBorder.h" 5 | #include "EditorStyleSet.h" 6 | #include "Widgets/SBoxPanel.h" 7 | #include "Widgets/Layout/SExpandableArea.h" 8 | #include "ContentBrowserModule.h" 9 | #include "IContentBrowserSingleton.h" 10 | #include "AssetRegistryModule.h" 11 | #include "Widgets/Input/SHyperlink.h" 12 | #include "Widgets/Layout/SSpacer.h" 13 | #include "IAssetTools.h" 14 | #include "AssetToolsModule.h" 15 | #include "Misc/MessageDialog.h" 16 | #include "Internationalization/Internationalization.h" 17 | #include "Widgets/Text/STextBlock.h" 18 | #include "Framework/Views/ITypedTableView.h" 19 | #include "UI/LintReportRuleErrorList.h" 20 | #include "LintRule.h" 21 | #include "AssetThumbnail.h" 22 | 23 | 24 | 25 | #define LOCTEXT_NAMESPACE "LintReport" 26 | 27 | void SLintReportRuleDetails::Construct(const FArguments& Args) 28 | { 29 | RuleViolations = Args._RuleViolations; 30 | ThumbnailPool = Args._ThumbnailPool; 31 | 32 | const float PaddingAmount = FLinterStyle::Get()->GetFloat("Linter.Padding"); 33 | 34 | check(RuleViolations.Get().Num() > 0 ); 35 | ULintRule* BrokenRule = (RuleViolations.Get())[0]->ViolatedRule.GetDefaultObject(); 36 | check(BrokenRule != nullptr); 37 | 38 | FText RuleName = BrokenRule->RuleTitle; 39 | FText RuleDesc = BrokenRule->RuleDescription; 40 | 41 | RuleURL = BrokenRule->RuleURL; 42 | 43 | const FSlateBrush* RuleIcon = nullptr; 44 | switch (BrokenRule->RuleSeverity) 45 | { 46 | case ELintRuleSeverity::Error: 47 | RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Error"); 48 | break; 49 | case ELintRuleSeverity::Warning: 50 | RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Warning"); 51 | break; 52 | case ELintRuleSeverity::Info: 53 | RuleIcon = FLinterStyle::Get()->GetBrush("Linter.Report.Info"); 54 | break; 55 | //case ELintRuleSeverity::Ignore: 56 | default: 57 | break; 58 | } 59 | 60 | FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); 61 | IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); 62 | 63 | RuleAssetData = AssetRegistry.GetAssetByObjectPath(FName(*BrokenRule->GetPathName()), true); 64 | FText RuleAssetPath; 65 | if (RuleAssetData.IsValid()) 66 | { 67 | RuleAssetPath = FText::FromName(RuleAssetData.PackagePath); 68 | } 69 | 70 | 71 | 72 | ChildSlot 73 | [ 74 | SNew(SBorder) 75 | .BorderImage(FEditorStyle::GetBrush("NoBorder")) 76 | .Padding(PaddingAmount) 77 | [ 78 | SNew(SBorder) 79 | .BorderImage(FEditorStyle::GetBrush("ToolPanel.GroupBorder")) 80 | .Padding(PaddingAmount) 81 | [ 82 | SNew(SVerticalBox) 83 | + SVerticalBox::Slot() 84 | .AutoHeight() 85 | .Padding(PaddingAmount) 86 | [ 87 | SNew(SExpandableArea) 88 | .InitiallyCollapsed(false) 89 | .HeaderContent() 90 | [ 91 | SNew(SHorizontalBox) 92 | // Rule Thumbnail 93 | + SHorizontalBox::Slot() 94 | .AutoWidth() 95 | .VAlign(VAlign_Top) 96 | .Padding(PaddingAmount) 97 | [ 98 | SAssignNew(ThumbnailBox, SBox) 99 | .WidthOverride(96.0f) 100 | .HeightOverride(96.0f) 101 | .Visibility(RuleAssetData.IsValid() ? EVisibility::HitTestInvisible : EVisibility::Collapsed) 102 | ] 103 | // Rule Icon 104 | + SHorizontalBox::Slot() 105 | .Padding(PaddingAmount) 106 | .AutoWidth() 107 | .VAlign(VAlign_Center) 108 | .HAlign(HAlign_Left) 109 | [ 110 | SNew(SBox) 111 | .WidthOverride(14.0f) 112 | .HeightOverride(14.0f) 113 | [ 114 | SNew(SImage) 115 | .Image(RuleIcon) 116 | ] 117 | ] 118 | // Rule Name 119 | + SHorizontalBox::Slot() 120 | .AutoWidth() 121 | [ 122 | SNew(STextBlock) 123 | .Text(RuleName) 124 | .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") 125 | ] 126 | // Link to Rule URL 127 | + SHorizontalBox::Slot() 128 | .AutoWidth() 129 | .Padding(8.0f, 0.0) 130 | [ 131 | SNew(SImage) 132 | .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) 133 | .Cursor(EMouseCursor::Hand) 134 | .Visibility(RuleURL.IsEmpty() ? EVisibility::Collapsed : EVisibility::Visible) 135 | .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) { FPlatformProcess::LaunchURL(*RuleURL, NULL, NULL); return FReply::Handled(); }) 136 | ] 137 | // Link to Rule Definition Asset 138 | + SHorizontalBox::Slot() 139 | .AutoWidth() 140 | .Padding(8.0f, 0.0) 141 | [ 142 | SNew(SImage) 143 | .Image(FLinterStyle::Get()->GetBrush("Linter.Report.Link")) 144 | .Cursor(EMouseCursor::Hand) 145 | .Visibility(EVisibility::Collapsed) 146 | .OnMouseButtonDown_Lambda([&](const FGeometry& Geo, const FPointerEvent& Event) 147 | { 148 | FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); 149 | ContentBrowserModule.Get().SyncBrowserToAssets(TArray({ RuleAssetData })); 150 | return FReply::Handled(); 151 | }) 152 | ] 153 | // Asset Count 154 | + SHorizontalBox::Slot() 155 | .VAlign(VAlign_Center) 156 | .AutoWidth() 157 | .Padding(8.0f, 0.0) 158 | [ 159 | SNew(SHyperlink) 160 | .Text(FText::FormatNamed(LOCTEXT("AssetCountDisplay", "{NumAssets} {NumAssets}|plural(one=Asset,other=Assets)"), TEXT("NumAssets"), RuleViolations.Get().Num())) 161 | .OnNavigate_Lambda([&]() 162 | { 163 | FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked("ContentBrowser"); 164 | TArray AssetDatas; 165 | 166 | for (const TSharedPtr& RuleViolation : RuleViolations.Get()) 167 | { 168 | AssetDatas.Push(RuleViolation->ViolatorAssetData); 169 | } 170 | 171 | ContentBrowserModule.Get().SyncBrowserToAssets(AssetDatas); 172 | }) 173 | ] 174 | ] 175 | .BodyContent() 176 | [ 177 | SNew(SVerticalBox) 178 | + SVerticalBox::Slot() 179 | .Padding(PaddingAmount) 180 | .HAlign(HAlign_Left) 181 | .AutoHeight() 182 | [ 183 | SNew(STextBlock) 184 | .Text(RuleDesc) 185 | .AutoWrapText(true) 186 | .TextStyle(FLinterStyle::Get(), "Linter.Report.AssetName") 187 | ] 188 | + SVerticalBox::Slot() 189 | .Padding(PaddingAmount) 190 | .HAlign(HAlign_Left) 191 | .AutoHeight() 192 | [ 193 | SNew(SLintReportRuleErrorList) 194 | .RuleViolations(RuleViolations) 195 | ] 196 | ] 197 | ] 198 | ] 199 | ] 200 | ]; 201 | 202 | if (RuleAssetData.IsValid()) 203 | { 204 | const TSharedPtr RuleThumbnail = MakeShareable(new FAssetThumbnail(RuleAssetData, 96, 96, ThumbnailPool.Get())); 205 | ThumbnailBox->SetContent(RuleThumbnail->MakeThumbnailWidget()); 206 | } 207 | } 208 | --------------------------------------------------------------------------------