master 8e39692cbf8e cached
12 files
53.0 KB
14.1k tokens
13 symbols
1 requests
Download .txt
Repository: dotBunny/CLionSourceCodeAccess
Branch: master
Commit: 8e39692cbf8e
Files: 12
Total size: 53.0 KB

Directory structure:
gitextract_r3vd351s/

├── .gitignore
├── CLionSourceCodeAccess.uplugin
├── README.md
├── Resources/
│   └── CLion-Unreal-CodeStyle.xml
└── Source/
    └── CLionSourceCodeAccess/
        ├── CLionSourceCodeAccess.Build.cs
        └── Private/
            ├── CLionSettings.cpp
            ├── CLionSettings.h
            ├── CLionSourceCodeAccessModule.cpp
            ├── CLionSourceCodeAccessModule.h
            ├── CLionSourceCodeAccessPrivatePCH.h
            ├── CLionSourceCodeAccessor.cpp
            └── CLionSourceCodeAccessor.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
UE4Editor-CLionSourceCodeAccess.dylib.ready
Intermediate
Binaries


================================================
FILE: CLionSourceCodeAccess.uplugin
================================================
{
	"FileVersion" : 3,
	"EngineVersion" : "4.18.0",
	"Version" : 107,
	"VersionName" : "1.07",
	"FriendlyName" : "CLion Integration",
	"Description" : "Allows access to source code in CLion.",
	"Category" : "Programming",
	"CreatedBy" : "dotBunny, Inc.",
	"CreatedByURL" : "http://dotbunny.com",
	"DocsURL" : "https://github.com/dotBunny/CLionSourceCodeAccess/wiki",
	"MarketplaceURL" : "com.epicgames.launcher://ue/marketplace/content/7a2e6de4c71a48c7b9df5f49b73e1a09",
	"SupportURL" : "https://github.com/dotBunny/CLionSourceCodeAccess/issues",
	"EnabledByDefault" : true,
	"CanContainContent" : false,
	"IsBetaVersion" : false,
	"Installed" : false,
	"Modules" :
	[
		{
			"Name" : "CLionSourceCodeAccess",
			"Type" : "Editor",
			"LoadingPhase" : "PostEngineInit",
			"WhitelistPlatforms" :
			[
				"Mac",
				"Linux",
				"Win64"
			]
		}
	]
}


================================================
FILE: README.md
================================================
# CLionSourceCodeAccess (UE4 <= 4.18)
A CLion Plugin for Unreal Engine

The plugin creates a fully flushed out CMakeList file for use with CLion, adding intellisense, compiler definitions, etc.
Please visit https://github.com/dotBunny/CLionSourceCodeAccess/wiki for information on how to install and use the plugin.
  
This will be the last release of the plugin seperate from Unreal Engine. To all those in the community that helped and contributed: **Thank You!** It is because of contributions like yours that projects like this thrive and grow.

# CLionSourceCodeAccess (UE4 >= 4.19)
As of Unreal Engine 4.19 the plugin has been merged into the UE4 source code and will be distributed with Unreal Engine. You can check it out the [repository](https://github.com/EpicGames/UnrealEngine/tree/master/Engine/Plugins/Developer/CLionSourceCodeAccess), and see some of the [changes](https://github.com/EpicGames/UnrealEngine/blob/master/Engine/Source/Programs/UnrealBuildTool/ProjectFiles/CMake/CMakefileGenerator.cs) done to UBT that made it possible.
  
## Mentions
[CLion Blog](https://blog.jetbrains.com/clion/2016/10/clion-and-ue4/)  
[UE Marketplace](https://www.unrealengine.com/marketplace/clion-integration)


================================================
FILE: Resources/CLion-Unreal-CodeStyle.xml
================================================
<code_scheme name="Unreal">
  <Objective-C>
    <option name="NAMESPACE_BRACE_PLACEMENT" value="2" />
    <option name="FUNCTION_BRACE_PLACEMENT" value="2" />
    <option name="BLOCK_BRACE_PLACEMENT" value="2" />
    <option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
    <option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
    <option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
    <option name="SPACE_AFTER_REFERENCE_IN_DECLARATION" value="true" />
    <option name="DISCHARGED_SHORT_TERNARY_OPERATOR" value="true" />
  </Objective-C>
  <Objective-C-extensions>
    <option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
    <option name="RELEASE_STYLE" value="IVAR" />
    <option name="TAG_PREFIX_OF_BLOCK_COMMENT" value="AT" />
    <option name="TAG_PREFIX_OF_LINE_COMMENT" value="BACK_SLASH" />
    <option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
    <file>
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
    </file>
    <class>
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
    </class>
    <extensions>
      <pair source="cpp" header="h" />
      <pair source="c" header="h" />
    </extensions>
  </Objective-C-extensions>
  <Objective-C-extensions>
    <option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
    <option name="RELEASE_STYLE" value="IVAR" />
    <option name="TAG_PREFIX_OF_BLOCK_COMMENT" value="AT" />
    <option name="TAG_PREFIX_OF_LINE_COMMENT" value="BACK_SLASH" />
    <option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
    <file>
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
    </file>
    <class>
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
      <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
    </class>
    <extensions>
      <pair source="cpp" header="h" />
      <pair source="c" header="h" />
    </extensions>
  </Objective-C-extensions>
  <codeStyleSettings language="ObjectiveC">
    <option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
    <option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
    <option name="BRACE_STYLE" value="2" />
    <option name="CLASS_BRACE_STYLE" value="2" />
    <option name="ELSE_ON_NEW_LINE" value="true" />
    <option name="ALIGN_MULTILINE_CHAINED_METHODS" value="true" />
    <option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="false" />
    <option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="false" />
    <option name="IF_BRACE_FORCE" value="3" />
    <option name="DOWHILE_BRACE_FORCE" value="3" />
    <option name="WHILE_BRACE_FORCE" value="3" />
    <option name="FOR_BRACE_FORCE" value="3" />
    <indentOptions>
      <option name="USE_TAB_CHARACTER" value="true" />
      <option name="SMART_TABS" value="true" />
    </indentOptions>
  </codeStyleSettings>
</code_scheme>

================================================
FILE: Source/CLionSourceCodeAccess/CLionSourceCodeAccess.Build.cs
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

namespace UnrealBuildTool.Rules
{
	public class CLionSourceCodeAccess : ModuleRules
	{
        public CLionSourceCodeAccess(ReadOnlyTargetRules Target) : base(Target)
		{
			PrivateDependencyModuleNames.AddRange(
				new string[]
				{
					"Core",
					"SourceCodeAccess",
					"DesktopPlatform",
                    "LevelEditor",
                    "XmlParser",
                    "HotReload",
				}
			);

            PublicDependencyModuleNames.AddRange(
                new string[]
                {
                    "Core",
                    "Engine",
                    "InputCore",
                    "UnrealEd",
                    "CoreUObject",
                    "Slate",
                    "SlateCore",
                    "WorkspaceMenuStructure",
                    "Projects",
                    "PropertyEditor",
                    "HotReload",
                    "Json",
                }
            );

            PCHUsage = PCHUsageMode.NoSharedPCHs;
		}
	}
}

================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSettings.cpp
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

#include "CLionSourceCodeAccessPrivatePCH.h"
#include "CLionSettings.h"

#if PLATFORM_WINDOWS
#include "AllowWindowsPlatformTypes.h"
#endif

#define LOCTEXT_NAMESPACE "CLionSourceCodeAccessor"

UCLionSettings::UCLionSettings(const FObjectInitializer& ObjectInitializer)
		: Super(ObjectInitializer)
{

}

bool UCLionSettings::CheckSettings()
{
#if PLATFORM_WINDOWS
	if (this->CLion.FilePath.IsEmpty())
	{
		// search from JetBrainsToolbox folder
		FString ToolboxBinPath;

		if (FWindowsPlatformMisc::QueryRegKey(HKEY_CURRENT_USER, TEXT("Software\\JetBrains s.r.o.\\JetBrainsToolbox\\"), TEXT(""), ToolboxBinPath)) {
			FPaths::NormalizeDirectoryName(ToolboxBinPath);
			FString PatternString(TEXT("(.*)/bin"));
			FRegexPattern Pattern(PatternString);
			FRegexMatcher Matcher(Pattern, ToolboxBinPath);
			if (Matcher.FindNext())
			{
				FString ToolboxPath = Matcher.GetCaptureGroup(1);

				FString SettingJsonPath = FPaths::Combine(ToolboxPath, FString(".settings.json"));
				if (FPaths::FileExists(SettingJsonPath))
				{
					FString JsonStr;
					FFileHelper::LoadFileToString(JsonStr, *SettingJsonPath);
					TSharedRef<TJsonReader<TCHAR>> JsonReader = TJsonReaderFactory<TCHAR>::Create(JsonStr);
					TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
					if (FJsonSerializer::Deserialize(JsonReader, JsonObject) && JsonObject.IsValid())
					{
						FString InstallLocation;
						if (JsonObject->TryGetStringField(TEXT("install_location"), InstallLocation))
						{
							if (!InstallLocation.IsEmpty())
							{
								ToolboxPath = InstallLocation;
							}
						}
					}
				}

				FString CLionHome = FPaths::Combine(ToolboxPath, FString("apps"), FString("CLion"));
				if (FPaths::DirectoryExists(CLionHome))
				{
					TArray<FString> IDEPaths;
					IFileManager::Get().FindFilesRecursive(IDEPaths, *CLionHome, TEXT("clion64.exe"), true, false);
					if (IDEPaths.Num() > 0)
					{
						this->CLion.FilePath = IDEPaths[0];
					}
				}
			}
		}
	}

	if (this->CLion.FilePath.IsEmpty())
	{
		// search from ProgID
		FString OpenCommand;

		if (!FWindowsPlatformMisc::QueryRegKey(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Classes\\Applications\\clion64.exe\\shell\\open\\command\\"), TEXT(""), OpenCommand)) {
			FWindowsPlatformMisc::QueryRegKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Classes\\Applications\\clion64.exe\\shell\\open\\command\\"), TEXT(""), OpenCommand);
		}

		FString PatternString(TEXT("\"(.*)\" \".*\""));
		FRegexPattern Pattern(PatternString);
		FRegexMatcher Matcher(Pattern, OpenCommand);
		if (Matcher.FindNext())
		{
			FString IDEPath = Matcher.GetCaptureGroup(1);
			if (FPaths::FileExists(IDEPath))
			{
				this->CLion.FilePath = IDEPath;
			}
		}
	}
#elif PLATFORM_MAC
	if (this->CLion.FilePath.IsEmpty())
	{
		NSURL* AppURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:@"com.jetbrains.CLion-EAP"];
		if (AppURL != nullptr)
		{
			this->CLion.FilePath = FString([AppURL path]);
		}
	}
	if (this->CLion.FilePath.IsEmpty())
	{
		NSURL* AppURL = [[NSWorkspace sharedWorkspace] URLForApplicationWithBundleIdentifier:@"com.jetbrains.CLion"];
		if (AppURL != nullptr)
		{
			this->CLion.FilePath = FString([AppURL path]);
		}
	}
	if (this->Mono.FilePath.IsEmpty())
	{
		FString MonoPath = FPaths::Combine(*FPaths::RootDir(), TEXT("Engine"), TEXT("Binaries"), TEXT("ThirdParty"), TEXT("Mono"), TEXT("Mac"), TEXT("bin"), TEXT("mono"));
		if (FPaths::FileExists(MonoPath))
		{
			this->Mono.FilePath = MonoPath;
		}
	}
	if (this->CCompiler.FilePath.IsEmpty())
	{
		if (FPaths::FileExists(TEXT("/usr/bin/clang")))
		{
			this->CCompiler.FilePath = TEXT("/usr/bin/clang");
		}
	}
	if (this->CXXCompiler.FilePath.IsEmpty())
	{
		if (FPaths::FileExists(TEXT("/usr/bin/clang++")))
		{
			this->CXXCompiler.FilePath = TEXT("/usr/bin/clang++");
		}
	}
#else
	if ( this->CLion.FilePath.IsEmpty())
	{
		if(FPaths::FileExists(TEXT("/opt/clion/bin/clion.sh")))
		{
			this->CLion.FilePath = TEXT("/opt/clion/bin/clion.sh");
		}
	}
	if (this->Mono.FilePath.IsEmpty() )
	{
		if (FPaths::FileExists(TEXT("/usr/bin/mono")))
		{
			this->Mono.FilePath = TEXT("/usr/bin/mono");
		}
		else if (FPaths::FileExists(TEXT("/opt/mono/bin/mono")))
		{
			this->Mono.FilePath = TEXT("/opt/mono/bin/mono");
		}
	}
	if (this->CCompiler.FilePath.IsEmpty())
	{
		if(FPaths::FileExists(TEXT("/usr/bin/clang")))
		{
			this->CCompiler.FilePath = TEXT("/usr/bin/clang");
		}
	}
	if (this->CXXCompiler.FilePath.IsEmpty())
	{
		if(FPaths::FileExists(TEXT("/usr/bin/clang++")))
		{
			this->CXXCompiler.FilePath = TEXT("/usr/bin/clang++");
		}
	}

#endif

	// Reset the setup complete before we check things
	this->bSetupComplete = true;

	if (this->CLion.FilePath.IsEmpty())
	{
		this->bSetupComplete = false;
	}

#if !PLATFORM_WINDOWS
	if (this->Mono.FilePath.IsEmpty())
	{
		this->bSetupComplete = false;
	}
#endif


	// Update CMakeList path
	this->CachedCMakeListPath = FPaths::Combine(*FPaths::ConvertRelativePathToFull(*FPaths::ProjectDir()),
	                                            TEXT("CMakeLists.txt"));

	return this->bSetupComplete;
}

FString UCLionSettings::GetCMakeListPath()
{
	return this->CachedCMakeListPath;
}

bool UCLionSettings::IsSetup()
{
	return this->bSetupComplete;
}


#if WITH_EDITOR

void UCLionSettings::PreEditChange(UProperty* PropertyAboutToChange)
{

	Super::PreEditChange(PropertyAboutToChange);

	// Cache our previous values
	this->PreviousCCompiler = this->CCompiler.FilePath;
	this->PreviousCLion = this->CLion.FilePath;
	this->PreviousCXXCompiler = this->CXXCompiler.FilePath;
#if !PLATFORM_WINDOWS
	this->PreviousMono = this->Mono.FilePath;
#endif

}

void UCLionSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
	const FName MemberPropertyName = (PropertyChangedEvent.Property != nullptr)
	                                 ? PropertyChangedEvent.MemberProperty->GetFName() : NAME_None;

	// CLion Executable Path Check
	if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UCLionSettings, CLion))
	{
		if (this->CLion.FilePath.IsEmpty())
		{
			this->bSetupComplete = false;
			return;
		}

		this->CLion.FilePath = FPaths::ConvertRelativePathToFull(this->CLion.FilePath);

		FText FailReason;

#if PLATFORM_MAC
		NSString *AppPath = [NSString stringWithUTF8String: TCHAR_TO_UTF8(*this->CLion.FilePath)];
		NSBundle *Bundle = [NSBundle bundleWithPath: AppPath];
		FString BundleId = FString([Bundle bundleIdentifier]);

		if (!BundleId.StartsWith(TEXT("com.jetbrains.CLion"), ESearchCase::CaseSensitive))
		{
			FailReason = LOCTEXT("CLionSelectMacApp", "Please select the CLion app");
			FMessageDialog::Open(EAppMsgType::Ok, FailReason);
			this->CLion.FilePath = this->PreviousCLion;
			return;
		}
		this->CLion.FilePath = FString([Bundle executablePath]);
#endif

		if (this->CLion.FilePath == this->PreviousCLion)
		{
			return;
		}

		if (!FPaths::ValidatePath(this->CLion.FilePath, &FailReason))
		{
			FMessageDialog::Open(EAppMsgType::Ok, FailReason);
			this->CLion.FilePath = this->PreviousCLion;
			if (this->CLion.FilePath.IsEmpty())
			{
				this->bSetupComplete = false;
			}
			return;
		}
	}

#if !PLATFORM_WINDOWS
	// Mono Path
	if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UCLionSettings, Mono))
	{
		if (this->Mono.FilePath.IsEmpty())
		{
			this->bSetupComplete = false;
			return;
		}

		this->Mono.FilePath = FPaths::ConvertRelativePathToFull(this->Mono.FilePath);

		FText FailReason;

		if (this->Mono.FilePath == this->PreviousMono)
		{
			return;
		}

		if (!FPaths::ValidatePath(this->Mono.FilePath, &FailReason))
		{
			FMessageDialog::Open(EAppMsgType::Ok, FailReason);
			this->Mono.FilePath = this->PreviousMono;
			return;
		}
	}
#endif


	// Check C Compiler Path
	if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UCLionSettings, CCompiler))
	{
		// Bail out if you've wiped it out
		if (this->CCompiler.FilePath.IsEmpty())
		{
			this->bRequireRefresh = true;
			return;
		}

		this->CCompiler.FilePath = FPaths::ConvertRelativePathToFull(this->CCompiler.FilePath);

		if (this->CCompiler.FilePath == this->PreviousCCompiler)
		{
			return;
		}

		if (!FPaths::FileExists(this->CCompiler.FilePath))
		{
			this->CCompiler.FilePath = this->PreviousCCompiler;
			return;
		}
		this->bRequireRefresh = true;
	}

	// Check C++ Compiler Path
	if (MemberPropertyName == GET_MEMBER_NAME_CHECKED(UCLionSettings, CXXCompiler))
	{
		if (this->CXXCompiler.FilePath.IsEmpty())
		{
			this->bRequireRefresh = true;
			return;
		}

		this->CXXCompiler.FilePath = FPaths::ConvertRelativePathToFull(this->CXXCompiler.FilePath);

		if (this->CXXCompiler.FilePath == this->PreviousCXXCompiler)
		{
			return;
		}

		if (!FPaths::FileExists(CXXCompiler.FilePath))
		{
			this->CXXCompiler.FilePath = this->PreviousCXXCompiler;
			return;
		}
		this->bRequireRefresh = true;
	}

	this->CheckSettings();
}

#endif




================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSettings.h
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

#pragma once

#include "CLionSourceCodeAccessPrivatePCH.h"
#include "CLionSettings.generated.h"

UCLASS(config = EditorUserSettings, defaultconfig)

class UCLionSettings : public UObject
{
	GENERATED_UCLASS_BODY()

public:

	/**
	 * Does the project files require a refresh based on changes made to the settings?
	 */
	bool bRequireRefresh = false;

	/**
	 * Check our settings, cache results and return
	 * @return Can the plugin be used?
	 */
	bool CheckSettings();

	/**
 	* Get the cached CMakeList path.
 	* @return CMakeList Path
 	*/
	FString GetCMakeListPath();

	/**
	* Are the settings for the plugin complete and usable?
	*/
	bool IsSetup();

	/**
	 * [optional] Path to a C compiler to be used in the CMakeList file.
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Compiler", Meta = (DisplayName = "C Compiler (Optional)"))
	FFilePath CCompiler;

	/**
	 * Path to CLion executable, used when needing to launch CLion.
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CLion", Meta = (DisplayName = "CLion Executable"))
	FFilePath CLion;

	/**
	 * [optional] Path to a C++ compiler to be used in the CMakeList file."
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Compiler", Meta = (DisplayName = "C++ Compiler (Optional)"))
	FFilePath CXXCompiler;

	/**
	* Target Configuration Debug
	*/
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Configurations", Meta = (DisplayName = "Debug"))
	bool bConfigureDebug = false;

	/**
	 * Target Configuration DebugGame
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Configurations", Meta = (DisplayName = "DebugGame"))
	bool bConfigureDebugGame = false;

	/**
	* Target Configuration Development
	*/
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Configurations", Meta = (DisplayName = "Development"))
	bool bConfigureDevelopment = true;

	/**
	 * Target Configuration Shipping
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Configurations", Meta = (DisplayName = "Shipping"))
	bool bConfigureShipping = false;

	/**
	 * Target Configuration Test
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Configurations", Meta = (DisplayName = "Test"))
	bool bConfigureTest = false;

	/**
	* General Visual Studio Code Project Files
	*/
	UPROPERTY(Config, EditAnywhere, Category = "Visual Studio Code", Meta = (DisplayName = "Generate c_cpp_properties.json File"))
	bool bGenerateVisualStudioCodeProject = false;
	
	/**
	 * Include Config In Makefile
	 */
	UPROPERTY(Config, EditAnywhere, Category = "Additional Folders", Meta = (DisplayName = "Include Configs"))
	bool bIncludeConfigs = false;

	/**
	 * Include Plugins In Makefile
	 */
	UPROPERTY(Config, EditAnywhere, Category = "Additional Folders", Meta = (DisplayName = "Include Plugins"))
	bool bIncludePlugins = false;

	/**
	 * Include Shaders In Makefile
	 */
	UPROPERTY(Config, EditAnywhere, Category = "Additional Folders", Meta = (DisplayName = "Include Shaders"))
	bool bIncludeShaders = false;

	/**
	 * Target Project Game Editor
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Projects", Meta = (DisplayName = "Project Game"))
	bool bProjectSpecificGame = false;
	/**
	 * Target Project Specific Editor
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Projects", Meta = (DisplayName = "Project Editor"))
	bool bProjectSpecificEditor = true;

	/**
	 * Target Project UE4 Editor
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Projects", Meta = (DisplayName = "UE4 Editor"))
	bool bProjectUE4Editor = false;
	/**
	 * Target Project UE4 Game
	 */
	UPROPERTY(Config, EditAnywhere, Category = "CMake Target Projects", Meta = (DisplayName = "UE4 Game"))
	bool bProjectUE4Game = false;

	// TODO: We can't !PLATFORM_WINDOWS this as UBT/UHT barfs
	/**
	 * Path to the Mono executable (Required on non-Windows platforms).
	 */
	UPROPERTY(Config, EditAnywhere, Category = "Unreal Engine", Meta = (DisplayName = "Mono Executable (Ignore On Windows)"))
	FFilePath Mono;

protected:
#if WITH_EDITOR

	virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;

	virtual void PreEditChange(UProperty* PropertyAboutToChange) override;

#endif

private:

	/**
	 * Cached flag to tell if all settings are present needed to use the plugin.
	 */
	bool bSetupComplete = false;

	/**
 	* A cached reference of the path to the CMakeList.txt file
 	*/
	FString CachedCMakeListPath;

	/**
     * Cached version of C Compiler location.
     */
	FString PreviousCCompiler;

	/**
     * Cached version of CLion location.
     */
	FString PreviousCLion;

	/**
     * Cached version of C++ Compiler location.
     */
	FString PreviousCXXCompiler;

	/**
	 * Cached version of Mono location.
	 */
	FString PreviousMono;
};

================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessModule.cpp
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

#include "CLionSourceCodeAccessPrivatePCH.h"
#include "Runtime/Core/Public/Features/IModularFeatures.h"
#include "Editor/LevelEditor/Public/LevelEditor.h"
#include "ISettingsModule.h"
#include "ISettingsSection.h"
#include "CLionSourceCodeAccessModule.h"
#include "CLionSettings.h"

#define LOCTEXT_NAMESPACE "CLionSourceCodeAccessor"

IMPLEMENT_MODULE(FCLionSourceCodeAccessModule, CLionSourceCodeAccess);

void FCLionSourceCodeAccessModule::AddMenuOptions(FMenuBuilder& MenuBuilder)
{
	MenuBuilder.BeginSection("CLionMenu", LOCTEXT("CLionMenuLabel", "CLion"));

	MenuBuilder.AddMenuEntry(
			LOCTEXT("CLionMenuGenerateCMakeListLabel", "Generate CMakeList"),
			LOCTEXT("CLionMenuGenerateCMakeListTooltip", "Generates the CMakeList file for the opened project."),
			FSlateIcon(),
			FUIAction(FExecuteAction::CreateRaw(this, &FCLionSourceCodeAccessModule::HandleGenerateProjectFiles)));

	MenuBuilder.AddMenuEntry(
			LOCTEXT("CLionMenuOpenCLionLabel", "Open CLion"),
			LOCTEXT("CLionMenuOpenCLionTooltip", "Generates the CMakeList file, and opens CLion."),
			FSlateIcon(),
			FUIAction(FExecuteAction::CreateRaw(this, &FCLionSourceCodeAccessModule::HandleOpenCLion)));

	MenuBuilder.EndSection();
}

void FCLionSourceCodeAccessModule::HandleGenerateProjectFiles()
{
	CLionSourceCodeAccessor.GenerateProjectFile();
}

void FCLionSourceCodeAccessModule::HandleOpenCLion()
{
	CLionSourceCodeAccessor.OpenSolution();
}

void FCLionSourceCodeAccessModule::RegisterMenu()
{
	if (FModuleManager::Get().IsModuleLoaded("LevelEditor"))
	{
		FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked<FLevelEditorModule>(
				TEXT("LevelEditor"));

		MainMenuExtender = MakeShareable(new FExtender);
		MainMenuExtender->AddMenuExtension("FileProject", EExtensionHook::After, NULL,
		                                   FMenuExtensionDelegate::CreateRaw(this,
		                                                                     &FCLionSourceCodeAccessModule::AddMenuOptions));

		LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MainMenuExtender);
	}

}

void FCLionSourceCodeAccessModule::RegisterSettings()
{
	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
	{
		SettingsModule->RegisterSettings("Project", "Plugins", "CLion", LOCTEXT("RuntimeSettingsName", "CLion"),
		                                 LOCTEXT("RuntimeSettingsDescription", "Configure the CLion Integration"),
		                                 GetMutableDefault<UCLionSettings>());
	}
}

void FCLionSourceCodeAccessModule::ShutdownModule()
{
	CLionSourceCodeAccessor.Shutdown();

	if (UObjectInitialized())
	{
		this->UnregisterSettings();
	}


	// unbind provider from editor
	IModularFeatures::Get().UnregisterModularFeature(TEXT("SourceCodeAccessor"), &CLionSourceCodeAccessor);
}

void FCLionSourceCodeAccessModule::StartupModule()
{

	// Register our custom settings
	this->RegisterSettings();

	// Register our custom menu additions
	this->RegisterMenu();

	// Start her up
	CLionSourceCodeAccessor.Startup();

	// Bind our source control provider to the editor
	IModularFeatures::Get().RegisterModularFeature(TEXT("SourceCodeAccessor"), &CLionSourceCodeAccessor);
}

bool FCLionSourceCodeAccessModule::SupportsDynamicReloading()
{
	// TODO: Until we have this all fixed up
	return false;
}

void FCLionSourceCodeAccessModule::UnregisterSettings()
{
	// Ensure to unregister all of your registered settings here, hot-reload would
	// otherwise yield unexpected results.
	if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
	{
		SettingsModule->UnregisterSettings("Project", "CLionSettings", "General");
	}
}

#undef LOCTEXT_NAMESPACE

================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessModule.h
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

#pragma once

#include "CLionSourceCodeAccessPrivatePCH.h"
#include "CLionSourceCodeAccessor.h"

class FCLionSourceCodeAccessModule : public IModuleInterface
{
public:
	/** IModuleInterface implementation */
	virtual void StartupModule() override;

	virtual void ShutdownModule() override;

	virtual bool SupportsDynamicReloading() override;

	void AddMenuOptions(FMenuBuilder& MenuBuilder);


private:
	TSharedPtr<FExtender> MainMenuExtender;
	FCLionSourceCodeAccessor CLionSourceCodeAccessor;

	void RegisterSettings();

	void RegisterMenu();

	void UnregisterSettings();

	void HandleGenerateProjectFiles();

	void HandleOpenCLion();
};

================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessPrivatePCH.h
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

#pragma once

#include "Core.h"
#include "UnrealEd.h"
#include "ModuleManager.h"
#include "ISourceCodeAccessModule.h"

================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessor.cpp
================================================
// Copyright 2017 dotBunny, Inc. All Rights Reserved.

#include "CLionSourceCodeAccessPrivatePCH.h"
#include "CLionSourceCodeAccessor.h"

#define LOCTEXT_NAMESPACE "CLionSourceCodeAccessor"

DEFINE_LOG_CATEGORY_STATIC(LogCLionAccessor, Log, All);

bool FCLionSourceCodeAccessor::AddSourceFiles(const TArray<FString>& AbsoluteSourcePaths,
                                              const TArray<FString>& AvailableModules)
{
	// There is no need to add the files to the make file because it already has wildcard searches of the necessary project folders
	return true;
}

bool FCLionSourceCodeAccessor::CanAccessSourceCode() const
{
	return this->Settings->IsSetup();
}

void FCLionSourceCodeAccessor::GenerateProjectFile()
{

	if (!this->Settings->IsSetup())
	{
		FMessageDialog::Open(EAppMsgType::Ok,
		                     LOCTEXT("SetupNotComplete", "The CLion plugin settings have not been completed."));
		return;
	}


	if (!FPaths::IsProjectFilePathSet())
	{
		FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ProjectFileNotFound", "A project file was not found."));
		return;
	}

	// Due to the currently broken production of CMakeFiles in UBT, we create a CodeLite project and convert it when
	// a viable CMakeList generation is available this will change.
	if (!this->GenerateFromCodeLiteProject())
	{
		FMessageDialog::Open(EAppMsgType::Ok, LOCTEXT("ProjectGenerationFailed", "Unable to produce project files"));
		this->Settings->bRequireRefresh = true;
	}
	else
	{
		this->Settings->bRequireRefresh = false;
	}
}

bool FCLionSourceCodeAccessor::GenerateFromCodeLiteProject()
{
	// Create our progress bar dialog.
	FScopedSlowTask ProjectGenerationTask(21, LOCTEXT("StartCMakeListGeneration", "Starting CMakeList Generation"));
	ProjectGenerationTask.MakeDialog();
	ProjectGenerationTask.EnterProgressFrame(1, LOCTEXT("StartCMakeListGeneration", "Starting CMakeList Generation"));

	// Cache our path information
	FString UnrealBuildToolPath = *FPaths::ConvertRelativePathToFull(
			*FPaths::Combine(*FPaths::EngineDir(), TEXT("Binaries"), TEXT("DotNET"), TEXT("UnrealBuildTool.exe")));
	FPaths::NormalizeFilename(UnrealBuildToolPath);
	FString ProjectFilePath = *FPaths::ConvertRelativePathToFull(*FPaths::GetProjectFilePath());
	FPaths::NormalizeFilename(ProjectFilePath);
	FString ProjectPath = *FPaths::ConvertRelativePathToFull(*FPaths::ProjectDir());
	FPaths::NormalizeFilename(ProjectPath);

	// Assign and filter our project name
	this->WorkingProjectName = FPaths::GetBaseFilename(ProjectFilePath, true);
	FPaths::NormalizeFilename(this->WorkingProjectName);

	// Reset our working folder, just incase
	this->WorkingMonoPath = "";

	// Start our master CMakeList file
	FString OutputTemplate = TEXT("cmake_minimum_required (VERSION 2.6)\nproject (UE4)\n");
	OutputTemplate.Append(TEXT("set(CMAKE_CXX_STANDARD 11)\n"));

  //Set Response files output
  OutputTemplate.Append(FString::Printf(TEXT("\nSET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1 CACHE BOOL \"\" FORCE)")));
  OutputTemplate.Append(FString::Printf(TEXT("\nSET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1 CACHE BOOL \"\" FORCE)\n")));

	// Handle CLang++ / CLang (If Defined)
	if (!this->Settings->CXXCompiler.FilePath.IsEmpty())
	{
		OutputTemplate.Append(
				FString::Printf(TEXT("set(CMAKE_CXX_COMPILER \"%s\")\n"), *this->Settings->CXXCompiler.FilePath));
	}
	if (!this->Settings->CCompiler.FilePath.IsEmpty())
	{
		OutputTemplate.Append(
				FString::Printf(TEXT("set(CMAKE_C_COMPILER \"%s\")\n"), *this->Settings->CCompiler.FilePath));
	}

	if (this->Settings->bGenerateVisualStudioCodeProject)
	{
		OutputTemplate.Append(TEXT("set(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n"));
	}

	// Add an empty line in the file, because we like organization.
	OutputTemplate.Append(TEXT("\n"));

	// Increase our progress
	ProjectGenerationTask.EnterProgressFrame(10, LOCTEXT("GeneratingCodLiteProject", "Generating CodeLite Project"));

#if PLATFORM_WINDOWS
	const FString BuildProjectCommand = *UnrealBuildToolPath;
	const FString BuildProjectParameters = FString::Printf(TEXT("\"%s\" -Game \"%s\" -OnlyPublic -CodeLiteFiles -CurrentPlatform -NoShippingConfigs"),
											   *ProjectFilePath,
											   *this->WorkingProjectName);
#else
	const FString BuildProjectCommand = *this->Settings->Mono.FilePath;
	const FString BuildProjectParameters = FString::Printf(
			TEXT("\"%s\" \"%s\" -Game \"%s\" -OnlyPublic -CodeLiteFiles -CurrentPlatform -NoShippingConfigs"),
			*UnrealBuildToolPath,
			*ProjectFilePath,
			*this->WorkingProjectName);
#endif

	FProcHandle BuildProjectProcess = FPlatformProcess::CreateProc(*BuildProjectCommand, *BuildProjectParameters, true,
	                                                               true, false, nullptr, 0, nullptr, nullptr);
	if (!BuildProjectProcess.IsValid())
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Failure to run UBT [%s %s]."), *BuildProjectCommand,
		       *BuildProjectParameters);
		FPlatformProcess::CloseProc(BuildProjectProcess);
		return false;
	}

	// Wait for the process to finish before moving on
	FPlatformProcess::WaitForProc(BuildProjectProcess);

	// Enter next phase of generation
	ProjectGenerationTask.EnterProgressFrame(1, LOCTEXT("CheckingFiles", "Checking Files"));

	// Setup path for Includes files
	FString IncludeDirectoriesPath = FPaths::Combine(*ProjectPath, *FString::Printf(TEXT("%sCodeCompletionFolders.txt"),
	                                                                                *this->WorkingProjectName));
	FPaths::NormalizeFilename(IncludeDirectoriesPath);
	if (!FPaths::FileExists(IncludeDirectoriesPath))
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Unable to find %s"), *IncludeDirectoriesPath);
		ProjectGenerationTask.EnterProgressFrame(9);
		return false;
	}

	// Setup path for Definitions file
	FString DefinitionsPath = FPaths::Combine(*ProjectPath, *FString::Printf(TEXT("%sCodeLitePreProcessor.txt"),
	                                                                         *this->WorkingProjectName));
	FPaths::NormalizeFilename(DefinitionsPath);
	if (!FPaths::FileExists(DefinitionsPath))
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Unable to find %s"), *DefinitionsPath);
		ProjectGenerationTask.EnterProgressFrame(9);
		return false;
	}

	// Setup path for our master project file
	FString GeneratedProjectFilePath = FPaths::Combine(*ProjectPath, *FString::Printf(TEXT("%s.workspace"),
	                                                                                  *this->WorkingProjectName));
	FPaths::NormalizeFilename(GeneratedProjectFilePath);
	if (!FPaths::FileExists(GeneratedProjectFilePath))
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Unable to find %s"), *GeneratedProjectFilePath);
		ProjectGenerationTask.EnterProgressFrame(9);
		return false;
	}

	// Setup path for where we will output the sub CMake files
	FString ProjectFileOutputFolder = FPaths::Combine(*ProjectPath, TEXT("Intermediate"), TEXT("ProjectFiles"));
	FPaths::NormalizeFilename(ProjectFileOutputFolder);
	if (!FPaths::DirectoryExists(ProjectFileOutputFolder))
	{
		FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*ProjectFileOutputFolder);
	}

	ProjectGenerationTask.EnterProgressFrame(3, LOCTEXT("CreatingHelperCMakeFiles", "Creating Helper CMake Files"));


	// Gather our information on include directories
	FString IncludeDirectoriesData;
	FFileHelper::LoadFileToString(IncludeDirectoriesData, *IncludeDirectoriesPath);
	TArray<FString> IncludeDirectoriesLines;
	IncludeDirectoriesData.ParseIntoArrayLines(IncludeDirectoriesLines, true);

	FString IncludeDirectoriesContent = TEXT("set(INCLUDE_DIRECTORIES \n");

	// Friendly requested helper for VS Code folks (this is not the main stay of the plugin, but it works)
	FString IncludeDirectoriesJSON = TEXT("");

	for (FString Line : IncludeDirectoriesLines)
	{
		FPaths::NormalizeFilename(Line);
		IncludeDirectoriesContent.Append(FString::Printf(TEXT("\t\"%s\"\n"), *Line));

		if ( this->Settings->bGenerateVisualStudioCodeProject ) {
			IncludeDirectoriesJSON.Append(FString::Printf(TEXT("\n\t\t\t\"%s\","), *Line));
		}
	}
	IncludeDirectoriesContent.Append(TEXT(")\ninclude_directories(${INCLUDE_DIRECTORIES})\n"));

	// Output our Include Directories content and add an entry to the CMakeList
	FString IncludeDirectoriesOutputPath = FPaths::Combine(*ProjectFileOutputFolder, TEXT("IncludeDirectories.cmake"));
	FPaths::NormalizeFilename(IncludeDirectoriesOutputPath);
	if (!FFileHelper::SaveStringToFile(IncludeDirectoriesContent, *IncludeDirectoriesOutputPath,
	                                   FFileHelper::EEncodingOptions::ForceAnsi))
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Error writing %s"), *IncludeDirectoriesOutputPath);
		return false;
	}
	OutputTemplate.Append(FString::Printf(TEXT("include(\"%s\")\n"), *IncludeDirectoriesOutputPath));


	// Output our VSCode project json file
	if ( this->Settings->bGenerateVisualStudioCodeProject )
	{

		// Output Path
		FString IncludeJSONOutputPath = FPaths::Combine(*ProjectPath, TEXT(".vscode"), TEXT("c_cpp_properties.json"));

		// Build Content
		FString JSONOutput = TEXT("");
#if PLATFORM_MAC
		JSONOutput.Append(TEXT("{\n\t\"configurations\": [\n\t{\n\t\t\"name\": \"Mac\",\n\t\t\"includePath\": ["));
#elif PLATFORM_LINUX
		JSONOutput.Append(TEXT("{\n\t\"configurations\": [\n\t{\n\t\t\"name\": \"Linux\",\n\t\t\"includePath\": ["));
#else
		JSONOutput.Append(TEXT("{\n\t\"configurations\": [\n\t{\n\t\t\"name\": \"Windows\",\n\t\t\"includePath\": ["));
#endif
		IncludeDirectoriesJSON.RemoveFromEnd(TEXT(","), ESearchCase::Type::IgnoreCase);

		JSONOutput.Append(IncludeDirectoriesJSON);
		JSONOutput.Append(TEXT("\n\t\t\t],\n\t\t\t\"browse\" : {\n\t\t\t\t\"limitSymbolsToIncludedHeaders\" : true, \n\t\t\t\t\"path\": ["));
		JSONOutput.Append(IncludeDirectoriesJSON);
		JSONOutput.Append(TEXT("\n\t\t\t\t]\n\t\t\t}\n\t\t}\n\t]\n}"));


		if (!FFileHelper::SaveStringToFile(JSONOutput, *IncludeJSONOutputPath,
		                                   FFileHelper::EEncodingOptions::ForceAnsi))
		{
			UE_LOG(LogCLionAccessor, Error, TEXT("Error writing %s"), *IncludeJSONOutputPath);
			return false;
		}

	}

	// Gather our information on definitions
	FString DefinitionsData;
	FFileHelper::LoadFileToString(DefinitionsData, *DefinitionsPath);
	TArray<FString> DefinitionsLines;
	DefinitionsData.ParseIntoArrayLines(DefinitionsLines, true);

	FString DefinitionsProcessed = TEXT("add_definitions(\n");
	for (FString Line : DefinitionsLines)
	{
		DefinitionsProcessed.Append(FString::Printf(TEXT("\t-D%s\n"), *Line));
	}
	DefinitionsProcessed.Append(TEXT(")\n"));

	// Output our Definitions content and add an entry to the CMakeList
	FString DefinitionsOutputPath = FPaths::Combine(*ProjectFileOutputFolder, TEXT("Definitions.cmake"));
	if (!FFileHelper::SaveStringToFile(DefinitionsProcessed, *DefinitionsOutputPath,
	                                   FFileHelper::EEncodingOptions::ForceAnsi))
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Error writing %s"), *DefinitionsOutputPath);
		return false;
	}
	OutputTemplate.Append(FString::Printf(TEXT("include(\"%s\")\n"), *DefinitionsOutputPath));


	ProjectGenerationTask.EnterProgressFrame(5, LOCTEXT("CreatingProjectCMakeFiles", "Creating Project CMake Files"));

	// Handle finding the project file (we'll use this to determine the subprojects)
	FXmlFile* RootGeneratedProjectFile = new FXmlFile();
	RootGeneratedProjectFile->LoadFile(*GeneratedProjectFilePath);
	const FXmlNode* RootProjectNode = RootGeneratedProjectFile->GetRootNode();
	const TArray<FXmlNode*> RootProjectNodes = RootProjectNode->GetChildrenNodes();
	TArray<FXmlNode*> ProjectNodes;

	// Iterate over nodes to come up with our project nodes
	for (FXmlNode* Node : RootProjectNodes)
	{
		if (Node->GetTag() == TEXT("Project"))
		{
			ProjectNodes.Add(Node);
		}
	}

	//WorkspaceConfiguration (DebugGame/Development/Shipping)

	// Iterate and create projects
	FXmlFile* WorkingGeneratedProjectFile = new FXmlFile();

	FScopedSlowTask SubProjectGenerationTask(ProjectNodes.Num(),
	                                         LOCTEXT("StartSubProjects", "Generating Sub Project File"));
	SubProjectGenerationTask.MakeDialog();


	// This is gonna function as a storage block of what we think the mono path (inside of HandleConfiguration)
	for (FXmlNode* Node : ProjectNodes)
	{
		// Increment Progress Bar
		FString OutputProjectTemplate = "";
		FString SubProjectName = Node->GetAttribute("Name");

		// Determine if we want this subproject
		if ((!this->Settings->bProjectSpecificEditor &&
		    (SubProjectName.Contains(this->WorkingProjectName) && SubProjectName.EndsWith("Editor"))) ||
		    (!this->Settings->bProjectSpecificGame && SubProjectName.Equals(this->WorkingProjectName)) ||
		    (!this->Settings->bProjectUE4Editor && SubProjectName.Equals("UE4Editor")) ||
		    (!this->Settings->bProjectUE4Game && SubProjectName.Equals("UE4Game")))
		{
			continue;
		}


		FString SubProjectFile = FPaths::Combine(*ProjectPath, *Node->GetAttribute("Path"));

		SubProjectGenerationTask.EnterProgressFrame(1);

		// Check the project file does exist
		if (!FPaths::FileExists(SubProjectFile))
		{
			UE_LOG(LogCLionAccessor, Warning, TEXT("Cannot find %s"), *SubProjectFile);
			continue;
		}

		FString ProjectFilesProcessed;

		WorkingGeneratedProjectFile->LoadFile(*SubProjectFile);

		// This is painful as we're going to have to iterate through each node/child till we have none
		FXmlNode* CurrentNode = WorkingGeneratedProjectFile->GetRootNode();

		// Call our recursive function to delve deep and get the data we need
		FString WorkingProjectFiles = this->GetAttributeByTagWithRestrictions(CurrentNode, TEXT("File"), TEXT("Name"));
		TArray<FString> WorkingProjectFilesLines;
		WorkingProjectFiles.ParseIntoArrayLines(WorkingProjectFilesLines, true);

		FString WorkingProjectFilesContent = TEXT("");
		for (FString Line : WorkingProjectFilesLines)
		{
			FPaths::NormalizeFilename(Line);
			WorkingProjectFilesContent.Append(FString::Printf(TEXT("%s\n"), *Line));
		}

		// Add file set to the project cmake file (this is so we split things up, so CLion does't have
		// any issues with the file size of one individual file.
		OutputProjectTemplate.Append(
				FString::Printf(TEXT("set(%s_FILES \n%s)\n"), *SubProjectName, *WorkingProjectFilesContent));

		// Time to output this, determine the output path
		FString ProjectOutputPath = FPaths::Combine(*ProjectFileOutputFolder,
		                                            *FString::Printf(TEXT("%s.cmake"), *SubProjectName));
		if (!FFileHelper::SaveStringToFile(OutputProjectTemplate, *ProjectOutputPath,
		                                   FFileHelper::EEncodingOptions::ForceAnsi))
		{
			UE_LOG(LogCLionAccessor, Error, TEXT("Error writing %s"), *ProjectOutputPath);
			return false;
		}

		// Add Include Of Project Files
		OutputTemplate.Append(FString::Printf(TEXT("include(\"%s\")\n"), *ProjectOutputPath));


		//Get working directory and build command
		FString CustomTargets = this->GetBuildCommands(CurrentNode, SubProjectName);
		OutputTemplate.Append(CustomTargets);

	}

	ProjectGenerationTask.EnterProgressFrame(1, LOCTEXT("CreatingCMakeListsFile", "Creating CMakeLists.txt File"));

	// Add Executable Definition To Main Template
	OutputTemplate.Append(
			FString::Printf(TEXT("add_executable(PleaseIgnoreMe ${%sEditor_FILES})"), *this->WorkingProjectName));

	// Write out the file
	if (!FFileHelper::SaveStringToFile(OutputTemplate, *this->Settings->GetCMakeListPath(),
	                                   FFileHelper::EEncodingOptions::ForceAnsi))
	{
		UE_LOG(LogCLionAccessor, Error, TEXT("Error writing %s"), *this->Settings->GetCMakeListPath());
		return false;
	}

	return true;
}

FString FCLionSourceCodeAccessor::GetAttributeByTagWithRestrictions(FXmlNode* CurrentNode, const FString& Tag,
                                                                    const FString& Attribute)
{
	FString ReturnContent = "";

	if (CurrentNode->GetTag() == Tag)
	{
		ReturnContent.Append(FString::Printf(TEXT("\t\"%s\"\n"), *CurrentNode->GetAttribute(Attribute)));
	}

	const TArray<FXmlNode*> childrenNodes = CurrentNode->GetChildrenNodes();
	for (FXmlNode* Node : childrenNodes)
	{
		//Don't get files from "Config", "Plugins", "Shaders"
		if (Node->GetTag() == TEXT("VirtualDirectory"))
		{
			const FString& name = Node->GetAttribute(TEXT("Name"));

			if (!this->Settings->bIncludeConfigs && name == TEXT("Config"))
			{
				continue;
			}
			if (!this->Settings->bIncludePlugins && name == TEXT("Plugins"))
			{
				continue;
			}
			if (!this->Settings->bIncludeShaders && name == TEXT("Shaders"))
			{
				continue;
			}
		}

		ReturnContent += FCLionSourceCodeAccessor::GetAttributeByTagWithRestrictions(Node, Tag, Attribute);
	}

	return ReturnContent;
}

FString FCLionSourceCodeAccessor::GetBuildCommands(FXmlNode* CurrentNode, const FString& SubprojectName)
{
	FString ReturnContent = "";

	if (CurrentNode->GetTag() != TEXT("Settings"))
	{
		const TArray<FXmlNode*> childrenNodes = CurrentNode->GetChildrenNodes();
		for (FXmlNode* Node : childrenNodes)
		{
			ReturnContent += FCLionSourceCodeAccessor::GetBuildCommands(Node, SubprojectName);
		}
	}
	else
	{
		const TArray<FXmlNode*> childrenNodes = CurrentNode->GetChildrenNodes();
		for (FXmlNode* Node : childrenNodes)
		{
			if (Node->GetTag() == TEXT("Configuration"))
			{
				ReturnContent += FCLionSourceCodeAccessor::HandleConfiguration(Node, SubprojectName);
			}
		}
	}

	return ReturnContent;
}

FString FCLionSourceCodeAccessor::HandleConfiguration(FXmlNode* CurrentNode, const FString& SubprojectName)
{
	FString ReturnContent = "";

	const FString& ConfigurationName = CurrentNode->GetAttribute(TEXT("Name"));

	FString WorkingDirectory = "";
	FString BuildCommand = "";
	FString CleanCommand = "";

	const TArray<FXmlNode*> childrenNodes = CurrentNode->GetChildrenNodes();
	for (FXmlNode* Node : childrenNodes)
	{

		if ((Node->GetTag() == TEXT("CustomBuild")) && (Node->GetAttribute(TEXT("Enabled")) == TEXT("yes")))
		{

			const TArray<FXmlNode*> subchildrenNodes = Node->GetChildrenNodes();

			for (FXmlNode* subNode : subchildrenNodes)
			{
				if (subNode->GetTag() == TEXT("WorkingDirectory"))
				{
					WorkingDirectory = subNode->GetContent();
					FPaths::NormalizeFilename(WorkingDirectory);
				}

				if (subNode->GetTag() == TEXT("BuildCommand"))
				{
					BuildCommand = subNode->GetContent();
					FPaths::NormalizeFilename(BuildCommand);
				}

				if (subNode->GetTag() == TEXT("CleanCommand"))
				{
					CleanCommand = subNode->GetContent();
					FPaths::NormalizeFilename(CleanCommand);
				}
			}

			if (!this->WorkingMonoPath.Equals(WorkingDirectory))
			{ // Do this to avoid duplication in CMakeLists.txt
				ReturnContent += FString::Printf(TEXT("\nset(MONO_ROOT_PATH \"%s\")\n"), *WorkingDirectory);
#if PLATFORM_WINDOWS
				ReturnContent += FString::Printf(TEXT("set(BUILD cd /d \"${MONO_ROOT_PATH}\")\n"));
#else
				ReturnContent += FString::Printf(TEXT("set(BUILD cd \"${MONO_ROOT_PATH}\")\n"));
#endif

				this->WorkingMonoPath = WorkingDirectory;
			}

			if ((ConfigurationName == TEXT("Debug") && !this->Settings->bConfigureDebug) ||
			    (ConfigurationName == TEXT("DebugGame") && !this->Settings->bConfigureDebugGame) ||
			    (ConfigurationName == TEXT("Development") && !this->Settings->bConfigureDevelopment) ||
			    (ConfigurationName == TEXT("Shipping") && !this->Settings->bConfigureShipping) ||
			    (ConfigurationName == TEXT("Test") && !this->Settings->bConfigureTest))
			{
			}
			else
			{
				ReturnContent += FString::Printf(TEXT("\n# Custom target for %s project, %s configuration\n"),
				                                 *SubprojectName, *ConfigurationName);
				ReturnContent += FString::Printf(TEXT("add_custom_target(%s-%s ${BUILD} && %s -game -progress)\n"),
				                                 *SubprojectName, *ConfigurationName, *BuildCommand);
				ReturnContent += FString::Printf(TEXT("add_custom_target(%s-%s-CLEAN ${BUILD} && %s)\n\n"),
				                                 *SubprojectName, *ConfigurationName, *CleanCommand);
			}
		}
	}

	return ReturnContent;
}

FText FCLionSourceCodeAccessor::GetDescriptionText() const
{
	return LOCTEXT("CLionDisplayDesc", "Open source code files in CLion");
}

FName FCLionSourceCodeAccessor::GetFName() const
{
	return FName("CLionSourceCodeAccessor");
}


FText FCLionSourceCodeAccessor::GetNameText() const
{
	return LOCTEXT("CLionDisplayName", "CLion");
}

bool FCLionSourceCodeAccessor::OpenFileAtLine(const FString& FullPath, int32 LineNumber, int32 ColumnNumber)
{
	if (!this->Settings->IsSetup())
	{
		UE_LOG(LogCLionAccessor, Warning, TEXT("Please configure the CLion integration in your project settings."));
		return false;
	}

	if (this->Settings->bRequireRefresh || !FPaths::FileExists(*this->Settings->GetCMakeListPath()))
	{
		this->GenerateProjectFile();
	}


	const FString Path = FString::Printf(TEXT("\"%s\" --line %d \"%s\""),
	                                     *FPaths::ConvertRelativePathToFull(*FPaths::ProjectDir()), LineNumber, *FullPath);



	FProcHandle Proc = FPlatformProcess::CreateProc(*this->Settings->CLion.FilePath, *Path, true, true, false, nullptr,
	                                                0, nullptr, nullptr);
	if (!Proc.IsValid())
	{
		UE_LOG(LogCLionAccessor, Warning, TEXT("Opening file (%s) at a specific line failed."), *Path);
		FPlatformProcess::CloseProc(Proc);
		return false;
	}

	return true;
}

bool FCLionSourceCodeAccessor::OpenSolution()
{
	if (!this->Settings->IsSetup())
	{
		UE_LOG(LogCLionAccessor, Warning, TEXT("Please configure the CLion integration in your project settings."));
		return false;
	}

	// TODO: Add check for CMakeProject file, if not there generate

	if (this->Settings->bRequireRefresh)
	{
		this->GenerateProjectFile();
	}

	if (this->Settings->bRequireRefresh || !FPaths::FileExists(*this->Settings->GetCMakeListPath()))
	{
		this->GenerateProjectFile();
	}

	const FString Path = FString::Printf(TEXT("\"%s\""), *FPaths::ConvertRelativePathToFull(*FPaths::ProjectDir()));
	if (FPlatformProcess::CreateProc(*this->Settings->CLion.FilePath, *Path, true, true, false, nullptr, 0, nullptr,
	                                 nullptr).IsValid())
	{
		// This seems to always fail no matter what we do - could be the process type? Get rid of the warning for now
		//UE_LOG(LogCLionAccessor, Warning, TEXT("Opening the solution failed."));
		return false;
	}
	return true;
}

bool FCLionSourceCodeAccessor::OpenSolutionAtPath(const FString& InSolutionPath) {
	// TODO: Add check for CMakeProject file, if not there generate

	if (this->Settings->bRequireRefresh)
	{
		this->GenerateProjectFile();
	}

	if (this->Settings->bRequireRefresh || !FPaths::FileExists(*this->Settings->GetCMakeListPath()))
	{
		this->GenerateProjectFile();
	}

	if (FPlatformProcess::CreateProc(*this->Settings->CLion.FilePath, *InSolutionPath, true, true, false, nullptr, 0, nullptr,
	                                 nullptr).IsValid())
	{
		// This seems to always fail no matter what we do - could be the process type? Get rid of the warning for now
		//UE_LOG(LogCLionAccessor, Warning, TEXT("Opening the solution failed."));
		return false;
	}
	return true;
}

bool FCLionSourceCodeAccessor::OpenSourceFiles(const TArray<FString>& AbsoluteSourcePaths)
{
	if (!this->Settings->IsSetup())
	{
		UE_LOG(LogCLionAccessor, Warning, TEXT("Please configure the CLion integration in your project settings."));
		return false;
	}

	if (this->Settings->bRequireRefresh || !FPaths::FileExists(*this->Settings->GetCMakeListPath()))
	{
		this->GenerateProjectFile();
	}


	FString sourceFilesList = "";

	// Build our paths based on what unreal sends to be opened
	for (const auto& SourcePath : AbsoluteSourcePaths)
	{
		sourceFilesList = FString::Printf(TEXT("%s \"%s\""), *sourceFilesList, *SourcePath);
	}

	// Trim any whitespace on our source file list
	sourceFilesList.TrimStartInline();
	sourceFilesList.TrimEndInline();

	FProcHandle Proc = FPlatformProcess::CreateProc(*this->Settings->CLion.FilePath, *sourceFilesList, true, false,
	                                                false, nullptr, 0, nullptr, nullptr);

	if (!Proc.IsValid())
	{
		UE_LOG(LogCLionAccessor, Warning, TEXT("Opening the source file (%s) failed."), *sourceFilesList);
		FPlatformProcess::CloseProc(Proc);
		return true;
	}

	return false;
}

bool FCLionSourceCodeAccessor::SaveAllOpenDocuments() const
{
	// TODO: Implement saving remotely?
	return true;
}

bool FCLionSourceCodeAccessor::DoesSolutionExist() const
{
	// TODO: check if the solution really exists
	return true;
}

void FCLionSourceCodeAccessor::Shutdown()
{
	this->Settings = nullptr;
}

void FCLionSourceCodeAccessor::Startup()
{
	// Get reference to our settings object
	this->Settings = GetMutableDefault<UCLionSettings>();

	this->Settings->CheckSettings();
}

#undef LOCTEXT_NAMESPACE


================================================
FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessor.h
================================================
// Copyright 2017 dotBunny Inc. All Rights Reserved.

#pragma once

#include "CLionSourceCodeAccessPrivatePCH.h"
#include "ISourceCodeAccessor.h"
#include "XmlParser.h"
#include "CLionSettings.h"

class FCLionSourceCodeAccessor : public ISourceCodeAccessor
{
public:
	/** ISourceCodeAccessor implementation */
	virtual void RefreshAvailability() override
	{
	}

	virtual bool CanAccessSourceCode() const override;

	virtual FName GetFName() const override;

	virtual FText GetNameText() const override;

	virtual FText GetDescriptionText() const override;

	virtual bool OpenSolution() override;

	virtual bool OpenSolutionAtPath(const FString& InSolutionPath) override;

	virtual bool OpenFileAtLine(const FString& FullPath, int32 LineNumber, int32 ColumnNumber = 0) override;

	virtual bool OpenSourceFiles(const TArray<FString>& AbsoluteSourcePaths) override;

	virtual bool
	AddSourceFiles(const TArray<FString>& AbsoluteSourcePaths, const TArray<FString>& AvailableModules) override;

	virtual bool SaveAllOpenDocuments() const override;

	virtual bool DoesSolutionExist() const override;

	/**
	 * Frame Tick (Not Used)
	 * @param DeltaTime of frame
	 */
	virtual void Tick(const float DeltaTime) override
	{
	}

	/**
 	* Create the CMakeLists.txt file and all the sub *.cmake addins.
 	*/
	void GenerateProjectFile();

	/**
 	* Deinitialize the accessor.
 	*/
	void Shutdown();

	/**
	 * Initialize the accessor.
	 */
	void Startup();

private:

	/**
	 * A local storage of the working Project name as we parse files
	 */
	FString WorkingProjectName;

	/**
     * A local reference to the Settings object.
     */
	UCLionSettings* Settings;

	/**
	 * A local storage of the working Mono path found while parsing files
	 */
	FString WorkingMonoPath;



	/**
	 * Instruct UnrealBuildTool to generate a CodeLite project, then convert it to CMakeList
	 * @return Was the project generation successful?
	 */
	bool GenerateFromCodeLiteProject();

	/**
	 * Recursively search XmlNode for children namd Tag, and grab their Attribute.
	 * @param The root XmlNode to search from.
	 * @param The tag of the elements to uses.
	 * @param The attribute that we want to collect.
	 * @return A CMakeList compatible string set of the attributes.
	 */
	FString GetAttributeByTagWithRestrictions(FXmlNode* CurrentNode, const FString& Tag, const FString& Attribute);

	FString GetBuildCommands(FXmlNode* CurrentNode, const FString& SubprojectName);

	FString HandleConfiguration(FXmlNode* CurrentNode, const FString& SubprojectName);
};
Download .txt
gitextract_r3vd351s/

├── .gitignore
├── CLionSourceCodeAccess.uplugin
├── README.md
├── Resources/
│   └── CLion-Unreal-CodeStyle.xml
└── Source/
    └── CLionSourceCodeAccess/
        ├── CLionSourceCodeAccess.Build.cs
        └── Private/
            ├── CLionSettings.cpp
            ├── CLionSettings.h
            ├── CLionSourceCodeAccessModule.cpp
            ├── CLionSourceCodeAccessModule.h
            ├── CLionSourceCodeAccessPrivatePCH.h
            ├── CLionSourceCodeAccessor.cpp
            └── CLionSourceCodeAccessor.h
Download .txt
SYMBOL INDEX (13 symbols across 6 files)

FILE: Source/CLionSourceCodeAccess/CLionSourceCodeAccess.Build.cs
  class CLionSourceCodeAccess (line 5) | public class CLionSourceCodeAccess : ModuleRules
    method CLionSourceCodeAccess (line 7) | public CLionSourceCodeAccess(ReadOnlyTargetRules Target) : base(Target)

FILE: Source/CLionSourceCodeAccess/Private/CLionSettings.cpp
  function FString (line 188) | FString UCLionSettings::GetCMakeListPath()
  type FPropertyChangedEvent (line 216) | struct FPropertyChangedEvent

FILE: Source/CLionSourceCodeAccess/Private/CLionSettings.h
  type FPropertyChangedEvent (line 142) | struct FPropertyChangedEvent

FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessModule.h
  function class (line 8) | class FCLionSourceCodeAccessModule : public IModuleInterface

FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessor.cpp
  function FString (line 389) | FString FCLionSourceCodeAccessor::GetAttributeByTagWithRestrictions(FXml...
  function FString (line 427) | FString FCLionSourceCodeAccessor::GetBuildCommands(FXmlNode* CurrentNode...
  function FString (line 454) | FString FCLionSourceCodeAccessor::HandleConfiguration(FXmlNode* CurrentN...
  function FText (line 528) | FText FCLionSourceCodeAccessor::GetDescriptionText() const
  function FName (line 533) | FName FCLionSourceCodeAccessor::GetFName() const
  function FText (line 539) | FText FCLionSourceCodeAccessor::GetNameText() const

FILE: Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessor.h
  function class (line 10) | class FCLionSourceCodeAccessor : public ISourceCodeAccessor
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (60K chars).
[
  {
    "path": ".gitignore",
    "chars": 66,
    "preview": "UE4Editor-CLionSourceCodeAccess.dylib.ready\nIntermediate\nBinaries\n"
  },
  {
    "path": "CLionSourceCodeAccess.uplugin",
    "chars": 850,
    "preview": "{\n\t\"FileVersion\" : 3,\n\t\"EngineVersion\" : \"4.18.0\",\n\t\"Version\" : 107,\n\t\"VersionName\" : \"1.07\",\n\t\"FriendlyName\" : \"CLion I"
  },
  {
    "path": "README.md",
    "chars": 1214,
    "preview": "# CLionSourceCodeAccess (UE4 <= 4.18)\nA CLion Plugin for Unreal Engine\n\nThe plugin creates a fully flushed out CMakeList"
  },
  {
    "path": "Resources/CLion-Unreal-CodeStyle.xml",
    "chars": 5218,
    "preview": "<code_scheme name=\"Unreal\">\n  <Objective-C>\n    <option name=\"NAMESPACE_BRACE_PLACEMENT\" value=\"2\" />\n    <option name=\""
  },
  {
    "path": "Source/CLionSourceCodeAccess/CLionSourceCodeAccess.Build.cs",
    "chars": 1052,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\nnamespace UnrealBuildTool.Rules\n{\n\tpublic class CLionSourceCodeAcc"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSettings.cpp",
    "chars": 8921,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\n#include \"CLionSourceCodeAccessPrivatePCH.h\"\n#include \"CLionSettin"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSettings.h",
    "chars": 4819,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CLionSourceCodeAccessPrivatePCH.h\"\n#includ"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessModule.cpp",
    "chars": 3778,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\n#include \"CLionSourceCodeAccessPrivatePCH.h\"\n#include \"Runtime/Cor"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessModule.h",
    "chars": 693,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CLionSourceCodeAccessPrivatePCH.h\"\n#includ"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessPrivatePCH.h",
    "chars": 171,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"Core.h\"\n#include \"UnrealEd.h\"\n#include \"Mo"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessor.cpp",
    "chars": 24918,
    "preview": "// Copyright 2017 dotBunny, Inc. All Rights Reserved.\n\n#include \"CLionSourceCodeAccessPrivatePCH.h\"\n#include \"CLionSourc"
  },
  {
    "path": "Source/CLionSourceCodeAccess/Private/CLionSourceCodeAccessor.h",
    "chars": 2523,
    "preview": "// Copyright 2017 dotBunny Inc. All Rights Reserved.\n\n#pragma once\n\n#include \"CLionSourceCodeAccessPrivatePCH.h\"\n#includ"
  }
]

About this extraction

This page contains the full source code of the dotBunny/CLionSourceCodeAccess GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (53.0 KB), approximately 14.1k tokens, and a symbol index with 13 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!