diff --git a/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs b/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs index 73d3705db..27a92cbc3 100644 --- a/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs +++ b/Source/Editor/Modules/SourceCodeEditing/InBuildSourceCodeEditor.cs @@ -57,6 +57,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing case CodeEditorTypes.VSCodeInsiders: Name = "Visual Studio Code - Insiders"; break; + case CodeEditorTypes.Rider: + Name = "Rider"; + break; default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } } @@ -73,6 +76,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing { case CodeEditorTypes.VSCodeInsiders: case CodeEditorTypes.VSCode: return "-vscode"; + case CodeEditorTypes.Rider: return "-vs2019"; default: return null; } } diff --git a/Source/Editor/Scripting/CodeEditor.cpp b/Source/Editor/Scripting/CodeEditor.cpp index 076bf5e0b..a95334554 100644 --- a/Source/Editor/Scripting/CodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditor.cpp @@ -5,6 +5,7 @@ #include "Engine/Platform/FileSystem.h" #include "ScriptsBuilder.h" #include "CodeEditors/VisualStudioCodeEditor.h" +#include "CodeEditors/RiderCodeEditor.h" #if USE_VISUAL_STUDIO_DTE #include "CodeEditors/VisualStudio/VisualStudioEditor.h" #endif @@ -241,6 +242,7 @@ bool CodeEditingManagerService::Init() VisualStudioEditor::FindEditors(&CodeEditors); #endif VisualStudioCodeEditor::FindEditors(&CodeEditors); + RiderCodeEditor::FindEditors(&CodeEditors); CodeEditors.Add(New()); return false; diff --git a/Source/Editor/Scripting/CodeEditor.h b/Source/Editor/Scripting/CodeEditor.h index 4f8923342..e53215713 100644 --- a/Source/Editor/Scripting/CodeEditor.h +++ b/Source/Editor/Scripting/CodeEditor.h @@ -43,7 +43,7 @@ API_ENUM(Namespace="FlaxEditor", Attributes="HideInEditor") enum class CodeEdito VS2013, /// - /// Visual Studio 2059 + /// Visual Studio 2015 /// VS2015, @@ -67,6 +67,11 @@ API_ENUM(Namespace="FlaxEditor", Attributes="HideInEditor") enum class CodeEdito /// VSCodeInsiders, + /// + /// Rider + /// + Rider, + MAX }; diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp new file mode 100644 index 000000000..4299b8865 --- /dev/null +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -0,0 +1,167 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "RiderCodeEditor.h" +#include "Engine/Platform/FileSystem.h" +#include "Engine/Core/Log.h" +#include "Editor/Editor.h" +#include "Editor/ProjectInfo.h" +#include "Editor/Scripting/ScriptsBuilder.h" +#include "Engine/Engine/Globals.h" + +#if PLATFORM_WINDOWS + +#include "Engine/Platform/File.h" +#include "Engine/Platform/Win32/IncludeWindowsHeaders.h" +#include "Engine/Serialization/Json.h" + +namespace +{ + bool FindRegistryKeyItems(HKEY hKey, Array& results) + { + Char nameBuffer[256]; + for (int32 i = 0;; i++) + { + const LONG result = RegEnumKeyW(hKey, i, nameBuffer, ARRAY_COUNT(nameBuffer)); + if (result == ERROR_NO_MORE_ITEMS) + break; + if (result != ERROR_SUCCESS) + return false; + results.Add(nameBuffer); + } + return true; + } + + void SearchDirectory(Array* output, const String& directory) + { + if (!FileSystem::DirectoryExists(directory)) + return; + + // Load product info + Array productInfoData; + const String productInfoPath = directory / TEXT("product-info.json"); + if (File::ReadAllBytes(productInfoPath, productInfoData)) + return; + rapidjson_flax::Document document; + document.Parse((char*)productInfoData.Get(), productInfoData.Count()); + if (document.HasParseError()) + return; + + // Find executable file path + auto launchMember = document.FindMember("launch"); + if (launchMember != document.MemberEnd() && launchMember->value.IsArray() && launchMember->value.Size() > 0) + { + auto launcherPathMember = launchMember->value[0].FindMember("launcherPath"); + if (launcherPathMember != launchMember->value[0].MemberEnd()) + { + auto launcherPath = launcherPathMember->value.GetText(); + auto exePath = directory / launcherPath; + if (launcherPath.HasChars() && FileSystem::FileExists(exePath)) + { + output->Add(New(exePath)); + } + } + } + } + + void SearchRegistry(Array* output, HKEY root, const Char* key) + { + // Open key + HKEY keyH; + if (RegOpenKeyExW(root, key, 0, KEY_READ, &keyH) != ERROR_SUCCESS) + return; + + // Iterate over subkeys + Array subKeys; + if (FindRegistryKeyItems(keyH, subKeys)) + { + for (auto& subKey : subKeys) + { + HKEY subKeyH; + if (RegOpenKeyExW(keyH, *subKey, 0, KEY_READ, &subKeyH) != ERROR_SUCCESS) + continue; + + // Read subkey value + DWORD type; + DWORD cbData; + if (RegQueryValueExW(subKeyH, TEXT(""), nullptr, &type, nullptr, &cbData) != ERROR_SUCCESS || type != REG_SZ) + { + RegCloseKey(subKeyH); + continue; + } + Array data; + data.Resize((int32)cbData / sizeof(Char)); + if (RegQueryValueExW(subKeyH, TEXT(""), nullptr, nullptr, reinterpret_cast(data.Get()), &cbData) != ERROR_SUCCESS) + { + RegCloseKey(subKeyH); + continue; + } + + // Check if it's a valid installation path + String path(data.Get(), data.Count() - 1); + SearchDirectory(output, path); + + RegCloseKey(subKeyH); + } + } + + RegCloseKey(keyH); + } +} + +#endif + +RiderCodeEditor::RiderCodeEditor(const String& execPath) + : _execPath(execPath) + , _solutionPath(Globals::ProjectFolder / Editor::Project->Name + TEXT(".sln")) +{ +} + +void RiderCodeEditor::FindEditors(Array* output) +{ +#if PLATFORM_WINDOWS + SearchRegistry(output, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + SearchRegistry(output, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); +#endif +} + +CodeEditorTypes RiderCodeEditor::GetType() const +{ + return CodeEditorTypes::Rider; +} + +String RiderCodeEditor::GetName() const +{ + return TEXT("Rider"); +} + +void RiderCodeEditor::OpenFile(const String& path, int32 line) +{ + // Generate project files if solution is missing + if (!FileSystem::FileExists(_solutionPath)) + { + ScriptsBuilder::GenerateProject(TEXT("-vs2019")); + } + + // Open file + line = line > 0 ? line : 1; + const String args = String::Format(TEXT("\"{0}\" --line {2} \"{1}\""), _solutionPath, path, line); + Platform::StartProcess(_execPath, args, StringView::Empty); +} + +void RiderCodeEditor::OpenSolution() +{ + // Generate project files if solution is missing + if (!FileSystem::FileExists(_solutionPath)) + { + ScriptsBuilder::GenerateProject(TEXT("-vs2019")); + } + + // Open solution + const String args = String::Format(TEXT("\"{0}\""), _solutionPath); + Platform::StartProcess(_execPath, args, StringView::Empty); +} + +void RiderCodeEditor::OnFileAdded(const String& path) +{ + ScriptsBuilder::GenerateProject(); +} diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.h b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.h new file mode 100644 index 000000000..20caac549 --- /dev/null +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.h @@ -0,0 +1,41 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Editor/Scripting/CodeEditor.h" + +/// +/// Implementation of code editor utility that is using Rider from JetBrains. +/// +class RiderCodeEditor : public CodeEditor +{ +private: + + String _execPath; + String _solutionPath; + +public: + + /// + /// Initializes a new instance of the class. + /// + /// Executable file path + RiderCodeEditor(const String& execPath); + +public: + + /// + /// Tries to find installed Visual Studio Code instance. Adds them to the result list. + /// + /// The output editors. + static void FindEditors(Array* output); + +public: + + // [CodeEditor] + CodeEditorTypes GetType() const override; + String GetName() const override; + void OpenFile(const String& path, int32 line) override; + void OpenSolution() override; + void OnFileAdded(const String& path) override; +}; diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 7cea15622..4dd2164ae 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -420,7 +420,7 @@ bool WindowsPlatform::ReadRegValue(void* root, const String& key, const String& } Array data; - data.Resize((int32)cbData); + data.Resize((int32)cbData / sizeof(Char)); if (RegQueryValueExW(hKey, *name, nullptr, nullptr, reinterpret_cast(data.Get()), &cbData) != ERROR_SUCCESS) { RegCloseKey(hKey);