292 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Tencent is pleased to support the open source community by making UnLua available.
 | 
						|
// 
 | 
						|
// Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
 | 
						|
//
 | 
						|
// Licensed under the MIT License (the "License"); 
 | 
						|
// you may not use this file except in compliance with the License. You may obtain a copy of the License at
 | 
						|
//
 | 
						|
// http://opensource.org/licenses/MIT
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, 
 | 
						|
// software distributed under the License is distributed on an "AS IS" BASIS, 
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 | 
						|
// See the License for the specific language governing permissions and limitations under the License.
 | 
						|
 | 
						|
#include "Misc/EngineVersionComparison.h"
 | 
						|
#include "UnLuaIntelliSenseGenerator.h"
 | 
						|
#if UE_VERSION_NEWER_THAN(5, 1, 0)
 | 
						|
#include "AssetRegistry/AssetRegistryModule.h"
 | 
						|
#else
 | 
						|
#include "AssetRegistryModule.h"
 | 
						|
#endif
 | 
						|
#include "CoreUObject.h"
 | 
						|
#include "UnLua.h"
 | 
						|
#include "UnLuaEditorSettings.h"
 | 
						|
#include "UnLuaIntelliSense.h"
 | 
						|
#include "WidgetBlueprint.h"
 | 
						|
#include "Blueprint/WidgetTree.h"
 | 
						|
#include "Engine/Blueprint.h"
 | 
						|
#include "Interfaces/IPluginManager.h"
 | 
						|
 | 
						|
#define LOCTEXT_NAMESPACE "UnLuaIntelliSenseGenerator"
 | 
						|
 | 
						|
TSharedPtr<FUnLuaIntelliSenseGenerator> FUnLuaIntelliSenseGenerator::Singleton;
 | 
						|
 | 
						|
TSharedRef<FUnLuaIntelliSenseGenerator> FUnLuaIntelliSenseGenerator::Get()
 | 
						|
{
 | 
						|
    if (!Singleton.IsValid())
 | 
						|
        Singleton = MakeShareable(new FUnLuaIntelliSenseGenerator);
 | 
						|
    return Singleton.ToSharedRef();
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::Initialize()
 | 
						|
{
 | 
						|
    if (bInitialized)
 | 
						|
        return;
 | 
						|
 | 
						|
    OutputDir = IPluginManager::Get().FindPlugin("UnLua")->GetBaseDir() + "/Intermediate/IntelliSense";
 | 
						|
 | 
						|
    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
 | 
						|
    AssetRegistryModule.Get().OnAssetAdded().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetAdded);
 | 
						|
    AssetRegistryModule.Get().OnAssetRemoved().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetRemoved);
 | 
						|
    AssetRegistryModule.Get().OnAssetRenamed().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetRenamed);
 | 
						|
    AssetRegistryModule.Get().OnAssetUpdated().AddRaw(this, &FUnLuaIntelliSenseGenerator::OnAssetUpdated);
 | 
						|
 | 
						|
    bInitialized = true;
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::UpdateAll()
 | 
						|
{
 | 
						|
    const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
 | 
						|
 | 
						|
    FARFilter Filter;
 | 
						|
#if UE_VERSION_OLDER_THAN(5, 1, 0)
 | 
						|
    Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
 | 
						|
    Filter.ClassNames.Add(UWidgetBlueprint::StaticClass()->GetFName());
 | 
						|
#else
 | 
						|
    Filter.ClassPaths.Add(UBlueprint::StaticClass()->GetClassPathName());
 | 
						|
    Filter.ClassPaths.Add(UWidgetBlueprint::StaticClass()->GetClassPathName());
 | 
						|
#endif
 | 
						|
 | 
						|
    TArray<FAssetData> BlueprintAssets;
 | 
						|
    TArray<const UField*> NativeTypes;
 | 
						|
    AssetRegistryModule.Get().GetAssets(Filter, BlueprintAssets);
 | 
						|
    CollectTypes(NativeTypes);
 | 
						|
 | 
						|
    auto TotalCount = BlueprintAssets.Num() + NativeTypes.Num();
 | 
						|
    if (TotalCount == 0)
 | 
						|
        return;
 | 
						|
 | 
						|
    TotalCount++;
 | 
						|
 | 
						|
    FScopedSlowTask SlowTask(TotalCount, LOCTEXT("GeneratingBlueprintsIntelliSense", "Generating Blueprints InstelliSense"));
 | 
						|
    SlowTask.MakeDialog();
 | 
						|
 | 
						|
    for (int32 i = 0; i < BlueprintAssets.Num(); i++)
 | 
						|
    {
 | 
						|
        if (SlowTask.ShouldCancel())
 | 
						|
            break;
 | 
						|
        OnAssetUpdated(BlueprintAssets[i]);
 | 
						|
        SlowTask.EnterProgressFrame();
 | 
						|
    }
 | 
						|
 | 
						|
    for (const auto Type : NativeTypes)
 | 
						|
    {
 | 
						|
        if (SlowTask.ShouldCancel())
 | 
						|
            break;
 | 
						|
 | 
						|
        Export(Type);
 | 
						|
        SlowTask.EnterProgressFrame();
 | 
						|
    }
 | 
						|
 | 
						|
    if (SlowTask.ShouldCancel())
 | 
						|
        return;
 | 
						|
    ExportUE(NativeTypes);
 | 
						|
    ExportUnLua();
 | 
						|
    SlowTask.EnterProgressFrame();
 | 
						|
}
 | 
						|
 | 
						|
bool FUnLuaIntelliSenseGenerator::IsBlueprint(const FAssetData& AssetData)
 | 
						|
{
 | 
						|
#if UE_VERSION_OLDER_THAN(5, 1, 0)
 | 
						|
    const FName AssetClass = AssetData.AssetClass;
 | 
						|
    return AssetClass == UBlueprint::StaticClass()->GetFName() || AssetClass == UWidgetBlueprint::StaticClass()->GetFName();
 | 
						|
#else
 | 
						|
    const auto AssetClassPath = AssetData.AssetClassPath.ToString();
 | 
						|
    return AssetClassPath == UBlueprint::StaticClass()->GetName() || AssetClassPath == UWidgetBlueprint::StaticClass()->GetName();
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
bool FUnLuaIntelliSenseGenerator::ShouldExport(const FAssetData& AssetData, bool bLoad)
 | 
						|
{
 | 
						|
    const auto& Settings = *GetDefault<UUnLuaEditorSettings>();
 | 
						|
    if (!Settings.bGenerateIntelliSense)
 | 
						|
        return false;
 | 
						|
 | 
						|
    if (!IsBlueprint(AssetData))
 | 
						|
        return false;
 | 
						|
 | 
						|
    const auto Asset = AssetData.FastGetAsset(bLoad);
 | 
						|
    if (!Asset)
 | 
						|
        return false;
 | 
						|
 | 
						|
    const auto Blueprint = Cast<UBlueprint>(Asset);
 | 
						|
    if (!Blueprint)
 | 
						|
        return false;
 | 
						|
 | 
						|
    if (Blueprint->SkeletonGeneratedClass || Blueprint->GeneratedClass)
 | 
						|
        return true;
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::Export(const UBlueprint* Blueprint)
 | 
						|
{
 | 
						|
    Export(Blueprint->GeneratedClass);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::Export(const UField* Field)
 | 
						|
{
 | 
						|
#if ENGINE_MAJOR_VERSION > 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 26)
 | 
						|
    const UPackage* Package = Field->GetPackage();
 | 
						|
#else
 | 
						|
    const UPackage* Package = (UPackage*)Field->GetTypedOuter(UPackage::StaticClass());
 | 
						|
#endif
 | 
						|
    auto ModuleName = Package->GetName();
 | 
						|
    if (!Field->IsNative())
 | 
						|
    {
 | 
						|
        int32 LastSlashIndex;
 | 
						|
        if (ModuleName.FindLastChar('/', LastSlashIndex))
 | 
						|
            ModuleName.LeftInline(LastSlashIndex);
 | 
						|
    }
 | 
						|
    FString FileName = UnLua::IntelliSense::GetTypeName(Field);
 | 
						|
    if (FileName.EndsWith("_C"))
 | 
						|
        FileName.LeftChopInline(2);
 | 
						|
    const FString Content = UnLua::IntelliSense::Get(Field);
 | 
						|
    SaveFile(ModuleName, FileName, Content);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::ExportUE(const TArray<const UField*> Types)
 | 
						|
{
 | 
						|
    const FString Content = UnLua::IntelliSense::GetUE(Types);
 | 
						|
    SaveFile("", "UE", Content);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::ExportUnLua()
 | 
						|
{
 | 
						|
    const auto ContentDir = IPluginManager::Get().FindPlugin(TEXT("UnLua"))->GetContentDir();
 | 
						|
    const auto SrcDir = ContentDir / "IntelliSense";
 | 
						|
    const auto DstDir = OutputDir;
 | 
						|
 | 
						|
    IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
 | 
						|
    if (!PlatformFile.DirectoryExists(*SrcDir))
 | 
						|
        return;
 | 
						|
 | 
						|
    PlatformFile.CopyDirectoryTree(*DstDir, *SrcDir, true);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::CollectTypes(TArray<const UField*>& Types)
 | 
						|
{
 | 
						|
    for (TObjectIterator<UClass> It; It; ++It)
 | 
						|
    {
 | 
						|
        const UClass* Class = *It;
 | 
						|
        const FString ClassName = Class->GetName();
 | 
						|
        // ReSharper disable StringLiteralTypo
 | 
						|
        if (ClassName.StartsWith("SKEL_")
 | 
						|
            || ClassName.StartsWith("PLACEHOLDER-CLASS")
 | 
						|
            || ClassName.StartsWith("REINST_")
 | 
						|
            || ClassName.StartsWith("TRASHCLASS_")
 | 
						|
            || ClassName.StartsWith("HOTRELOADED_")
 | 
						|
        )
 | 
						|
        {
 | 
						|
            // skip nonsense types
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        // ReSharper restore StringLiteralTypo
 | 
						|
        Types.Add(Class);
 | 
						|
    }
 | 
						|
 | 
						|
    for (TObjectIterator<UScriptStruct> It; It; ++It)
 | 
						|
    {
 | 
						|
        const UScriptStruct* ScriptStruct = *It;
 | 
						|
        Types.Add(ScriptStruct);
 | 
						|
    }
 | 
						|
 | 
						|
    for (TObjectIterator<UEnum> It; It; ++It)
 | 
						|
    {
 | 
						|
        const UEnum* Enum = *It;
 | 
						|
        Types.Add(Enum);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::SaveFile(const FString& ModuleName, const FString& FileName, const FString& GeneratedFileContent)
 | 
						|
{
 | 
						|
    IFileManager& FileManager = IFileManager::Get();
 | 
						|
    const FString Directory = OutputDir / ModuleName;
 | 
						|
    if (!FileManager.DirectoryExists(*Directory))
 | 
						|
        FileManager.MakeDirectory(*Directory);
 | 
						|
 | 
						|
    const FString FilePath = FString::Printf(TEXT("%s/%s.lua"), *Directory, *FileName);
 | 
						|
    FString FileContent;
 | 
						|
    FFileHelper::LoadFileToString(FileContent, *FilePath);
 | 
						|
    if (FileContent != GeneratedFileContent)
 | 
						|
        FFileHelper::SaveStringToFile(GeneratedFileContent, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::DeleteFile(const FString& ModuleName, const FString& FileName)
 | 
						|
{
 | 
						|
    IFileManager& FileManager = IFileManager::Get();
 | 
						|
    const FString Directory = OutputDir / ModuleName;
 | 
						|
    if (!FileManager.DirectoryExists(*Directory))
 | 
						|
        FileManager.MakeDirectory(*Directory);
 | 
						|
 | 
						|
    const FString FilePath = FString::Printf(TEXT("%s/%s.lua"), *Directory, *FileName);
 | 
						|
    if (FileManager.FileExists(*FilePath))
 | 
						|
        FileManager.Delete(*FilePath);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::OnAssetAdded(const FAssetData& AssetData)
 | 
						|
{
 | 
						|
    if (!ShouldExport(AssetData))
 | 
						|
        return;
 | 
						|
 | 
						|
    OnAssetUpdated(AssetData);
 | 
						|
 | 
						|
    TArray<const UField*> Types;
 | 
						|
    CollectTypes(Types);
 | 
						|
    ExportUE(Types);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::OnAssetRemoved(const FAssetData& AssetData)
 | 
						|
{
 | 
						|
    if (!ShouldExport(AssetData))
 | 
						|
        return;
 | 
						|
 | 
						|
    DeleteFile(FString("/Game"), AssetData.AssetName.ToString());
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::OnAssetRenamed(const FAssetData& AssetData, const FString& OldPath)
 | 
						|
{
 | 
						|
    if (!ShouldExport(AssetData))
 | 
						|
        return;
 | 
						|
 | 
						|
    //remove old Blueprint name
 | 
						|
    const FString OldPackageName = FPackageName::GetShortName(OldPath);
 | 
						|
    DeleteFile("/Game", OldPackageName);
 | 
						|
 | 
						|
    //update new name 
 | 
						|
    OnAssetUpdated(AssetData);
 | 
						|
}
 | 
						|
 | 
						|
void FUnLuaIntelliSenseGenerator::OnAssetUpdated(const FAssetData& AssetData)
 | 
						|
{
 | 
						|
    if (!ShouldExport(AssetData, true))
 | 
						|
        return;
 | 
						|
 | 
						|
    UBlueprint* Blueprint = LoadObject<UBlueprint>(nullptr, *AssetData.ObjectPath.ToString());
 | 
						|
    if (!Blueprint)
 | 
						|
        return;
 | 
						|
 | 
						|
    Export(Blueprint);
 | 
						|
}
 |