Add example Unity Project

This commit is contained in:
Michał Gdula 2023-04-26 01:55:33 +01:00
parent fda7ff28dd
commit e3acdb9d6b
7122 changed files with 505543 additions and 2 deletions

View file

@ -0,0 +1,6 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf

View file

@ -0,0 +1,79 @@
# Code Editor Package for Rider
## [1.2.1] - 2019-12-09
- Load optimised EditorPlugin version compiled to net 461, with fallback to previous version.
- On ExternalEditor settings page: reorder Generate all ... after Extensions handled
- Better presentation for Rider of some version in ExternalEditors list
- Initial support for Code Coverage with dotCover plugin in Rider
- Added support for Player Project generation
## [1.1.4] - 2019-11-21
- Fix warning - unreachable code
## [1.1.3] - 2019-10-17
- Update External Editor, when new toolbox build was installed
- Add xaml to default list of extensions to include in csproj
- Avoid initializing Rider package in secondary Unity process, which does Asset processing
- Reflect multiple csc.rsp arguments to generated csproj files: https://github.com/JetBrains/resharper-unity/issues/1337
- Setting, which allowed to override LangVersion removed in favor of langversion in csc.rsp
- Environment.NewLine is used in generated project files instead of Windows line separator.
## [1.1.2] - 2019-09-18
performance optimizations:
- avoid multiple evaluations
- avoid reflection in DisableSyncSolutionOnceCallBack
- project generation optimization
fixes:
- avoid compilation error with incompatible `Test Framework` package
## [1.1.1] - 2019-08-26
parse nowarn in csc.rsp
warning, when Unity was started from Rider, but external editor was different
improved unit test support
workaround to avoid Unity internal project-generation (fix #28)
## [1.1.0] - 2019-07-02
new setting to manage list of extensions to be opened with Rider
avoid breaking everything on any unhandled exception in RiderScriptEditor cctor
hide Rider settings, when different Editor is selected
dynamically load only newer rider plugins
path detection (work on unix symlinks)
speed up for project generation
lots of bug fixing
## [1.0.8] - 2019-05-20
Fix NullReferenceException when External editor was pointing to non-existing Rider everything was broken by null-ref.
## [1.0.7] - 2019-05-16
Initial migration steps from rider plugin to package.
Fix OSX check and opening of files.
## [1.0.6] - 2019-04-30
Ensure asset database is refreshed when generating csproj and solution files.
## [1.0.5] - 2019-04-27
Add support for generating all csproj files.
## [1.0.4] - 2019-04-18
Fix relative package paths.
Fix opening editor on mac.
## [1.0.3] - 2019-04-12
Fixing null reference issue for callbacks to Asset pipeline.
## [1.0.2] - 2019-01-01
### This is the first release of *Unity Package rider_editor*.
Using the newly created api to integrate Rider with Unity.

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8645aa9c3c74fb34ba9499e14fb332b5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,6 @@
# Contributing
## All contributions are subject to the [Unity Contribution Agreement(UCA)](https://unity3d.com/legal/licenses/Unity_Contribution_Agreement)
By making a pull request, you are confirming agreement to the terms and conditions of the UCA, including that your Contributions are your original creation and that you have complete right and authority to make your Contributions.
## Once you have a change ready following these ground rules. Simply make a pull request

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5e83f8baac96eaa47bdd9ca781cd2002
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Unity Technologies
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5598b14661b5f4c43bed757f34b6d172
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9129183a42052cd43b9c284d6dbd541e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1b393f6b29a9ee84c803af1ab4944b71
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,509 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Win32;
using Unity.CodeEditor;
using UnityEngine;
namespace Packages.Rider.Editor
{
public interface IDiscovery
{
CodeEditor.Installation[] PathCallback();
}
public class Discovery : IDiscovery
{
public CodeEditor.Installation[] PathCallback()
{
return RiderPathLocator.GetAllRiderPaths()
.Select(riderInfo => new CodeEditor.Installation
{
Path = riderInfo.Path,
Name = riderInfo.Presentation
})
.OrderBy(a=>a.Name)
.ToArray();
}
}
/// <summary>
/// This code is a modified version of the JetBrains resharper-unity plugin listed here:
/// https://github.com/JetBrains/resharper-unity/blob/master/unity/JetBrains.Rider.Unity.Editor/EditorPlugin/RiderPathLocator.cs
/// </summary>
public static class RiderPathLocator
{
#if !(UNITY_4_7 || UNITY_5_5)
[UsedImplicitly] // Used in com.unity.ide.rider
public static RiderInfo[] GetAllRiderPaths()
{
try
{
switch (SystemInfo.operatingSystemFamily)
{
case OperatingSystemFamily.Windows:
{
return CollectRiderInfosWindows();
}
case OperatingSystemFamily.MacOSX:
{
return CollectRiderInfosMac();
}
case OperatingSystemFamily.Linux:
{
return CollectAllRiderPathsLinux();
}
}
}
catch (Exception e)
{
Debug.LogException(e);
}
return new RiderInfo[0];
}
#endif
#if RIDER_EDITOR_PLUGIN // can't be used in com.unity.ide.rider
internal static RiderInfo[] GetAllFoundInfos(OperatingSystemFamilyRider operatingSystemFamily)
{
try
{
switch (operatingSystemFamily)
{
case OperatingSystemFamilyRider.Windows:
{
return CollectRiderInfosWindows();
}
case OperatingSystemFamilyRider.MacOSX:
{
return CollectRiderInfosMac();
}
case OperatingSystemFamilyRider.Linux:
{
return CollectAllRiderPathsLinux();
}
}
}
catch (Exception e)
{
Debug.LogException(e);
}
return new RiderInfo[0];
}
internal static string[] GetAllFoundPaths(OperatingSystemFamilyRider operatingSystemFamily)
{
return GetAllFoundInfos(operatingSystemFamily).Select(a=>a.Path).ToArray();
}
#endif
private static RiderInfo[] CollectAllRiderPathsLinux()
{
var installInfos = new List<RiderInfo>();
var home = Environment.GetEnvironmentVariable("HOME");
if (!string.IsNullOrEmpty(home))
{
var toolboxRiderRootPath = GetToolboxBaseDir();
installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false)
.Select(a => new RiderInfo(a, true)).ToList());
//$Home/.local/share/applications/jetbrains-rider.desktop
var shortcut = new FileInfo(Path.Combine(home, @".local/share/applications/jetbrains-rider.desktop"));
if (shortcut.Exists)
{
var lines = File.ReadAllLines(shortcut.FullName);
foreach (var line in lines)
{
if (!line.StartsWith("Exec=\""))
continue;
var path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
if (string.IsNullOrEmpty(path))
continue;
if (installInfos.Any(a => a.Path == path)) // avoid adding similar build as from toolbox
continue;
installInfos.Add(new RiderInfo(path, false));
}
}
}
// snap install
var snapInstallPath = "/snap/rider/current/bin/rider.sh";
if (new FileInfo(snapInstallPath).Exists)
installInfos.Add(new RiderInfo(snapInstallPath, false));
return installInfos.ToArray();
}
private static RiderInfo[] CollectRiderInfosMac()
{
var installInfos = new List<RiderInfo>();
// "/Applications/*Rider*.app"
var folder = new DirectoryInfo("/Applications");
if (folder.Exists)
{
installInfos.AddRange(folder.GetDirectories("*Rider*.app")
.Select(a => new RiderInfo(a.FullName, false))
.ToList());
}
// /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app
var toolboxRiderRootPath = GetToolboxBaseDir();
var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true)
.Select(a => new RiderInfo(a, true));
installInfos.AddRange(paths);
return installInfos.ToArray();
}
private static RiderInfo[] CollectRiderInfosWindows()
{
var installInfos = new List<RiderInfo>();
var toolboxRiderRootPath = GetToolboxBaseDir();
var installPathsToolbox = CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider64.exe", false).ToList();
installInfos.AddRange(installPathsToolbox.Select(a => new RiderInfo(a, true)).ToList());
var installPaths = new List<string>();
const string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
CollectPathsFromRegistry(registryKey, installPaths);
const string wowRegistryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
CollectPathsFromRegistry(wowRegistryKey, installPaths);
installInfos.AddRange(installPaths.Select(a => new RiderInfo(a, false)).ToList());
return installInfos.ToArray();
}
private static string GetToolboxBaseDir()
{
switch (SystemInfo.operatingSystemFamily)
{
case OperatingSystemFamily.Windows:
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return Path.Combine(localAppData, @"JetBrains\Toolbox\apps\Rider");
}
case OperatingSystemFamily.MacOSX:
{
var home = Environment.GetEnvironmentVariable("HOME");
if (!string.IsNullOrEmpty(home))
{
return Path.Combine(home, @"Library/Application Support/JetBrains/Toolbox/apps/Rider");
}
break;
}
case OperatingSystemFamily.Linux:
{
var home = Environment.GetEnvironmentVariable("HOME");
if (!string.IsNullOrEmpty(home))
{
return Path.Combine(home, @".local/share/JetBrains/Toolbox/apps/Rider");
}
break;
}
}
return string.Empty;
}
internal static ProductInfo GetBuildVersion(string path)
{
var buildTxtFileInfo = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
var dir = buildTxtFileInfo.DirectoryName;
if (!Directory.Exists(dir))
return null;
var buildVersionFile = new FileInfo(Path.Combine(dir, "product-info.json"));
if (!buildVersionFile.Exists)
return null;
var json = File.ReadAllText(buildVersionFile.FullName);
return ProductInfo.GetProductInfo(json);
}
internal static Version GetBuildNumber(string path)
{
var file = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
if (!file.Exists)
return null;
var text = File.ReadAllText(file.FullName);
if (text.Length <= 3)
return null;
var versionText = text.Substring(3);
return Version.TryParse(versionText, out var v) ? v : null;
}
internal static bool IsToolbox(string path)
{
return path.StartsWith(GetToolboxBaseDir());
}
private static string GetRelativePathToBuildTxt()
{
switch (SystemInfo.operatingSystemFamily)
{
case OperatingSystemFamily.Windows:
case OperatingSystemFamily.Linux:
return "../../build.txt";
case OperatingSystemFamily.MacOSX:
return "Contents/Resources/build.txt";
}
throw new Exception("Unknown OS");
}
private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
{
using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
{
if (key == null) return;
foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
{
using (var subkey = key.OpenSubKey(subkeyName))
{
var folderObject = subkey?.GetValue("InstallLocation");
if (folderObject == null) continue;
var folder = folderObject.ToString();
var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
if (File.Exists(possiblePath))
installPaths.Add(possiblePath);
}
}
}
}
private static string[] CollectPathsFromToolbox(string toolboxRiderRootPath, string dirName, string searchPattern,
bool isMac)
{
if (!Directory.Exists(toolboxRiderRootPath))
return new string[0];
var channelDirs = Directory.GetDirectories(toolboxRiderRootPath);
var paths = channelDirs.SelectMany(channelDir =>
{
try
{
// use history.json - last entry stands for the active build https://jetbrains.slack.com/archives/C07KNP99D/p1547807024066500?thread_ts=1547731708.057700&cid=C07KNP99D
var historyFile = Path.Combine(channelDir, ".history.json");
if (File.Exists(historyFile))
{
var json = File.ReadAllText(historyFile);
var build = ToolboxHistory.GetLatestBuildFromJson(json);
if (build != null)
{
var buildDir = Path.Combine(channelDir, build);
var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
if (executablePaths.Any())
return executablePaths;
}
}
var channelFile = Path.Combine(channelDir, ".channel.settings.json");
if (File.Exists(channelFile))
{
var json = File.ReadAllText(channelFile).Replace("active-application", "active_application");
var build = ToolboxInstallData.GetLatestBuildFromJson(json);
if (build != null)
{
var buildDir = Path.Combine(channelDir, build);
var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
if (executablePaths.Any())
return executablePaths;
}
}
// changes in toolbox json files format may brake the logic above, so return all found Rider installations
return Directory.GetDirectories(channelDir)
.SelectMany(buildDir => GetExecutablePaths(dirName, searchPattern, isMac, buildDir));
}
catch (Exception e)
{
// do not write to Debug.Log, just log it.
Logger.Warn($"Failed to get RiderPath from {channelDir}", e);
}
return new string[0];
})
.Where(c => !string.IsNullOrEmpty(c))
.ToArray();
return paths;
}
private static string[] GetExecutablePaths(string dirName, string searchPattern, bool isMac, string buildDir)
{
var folder = new DirectoryInfo(Path.Combine(buildDir, dirName));
if (!folder.Exists)
return new string[0];
if (!isMac)
return new[] {Path.Combine(folder.FullName, searchPattern)}.Where(File.Exists).ToArray();
return folder.GetDirectories(searchPattern).Select(f => f.FullName)
.Where(Directory.Exists).ToArray();
}
// Disable the "field is never assigned" compiler warning. We never assign it, but Unity does.
// Note that Unity disable this warning in the generated C# projects
#pragma warning disable 0649
[Serializable]
class ToolboxHistory
{
public List<ItemNode> history;
[CanBeNull]
public static string GetLatestBuildFromJson(string json)
{
try
{
#if UNITY_4_7 || UNITY_5_5
return JsonConvert.DeserializeObject<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
#else
return JsonUtility.FromJson<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
#endif
}
catch (Exception)
{
Logger.Warn($"Failed to get latest build from json {json}");
}
return null;
}
}
[Serializable]
class ItemNode
{
public BuildNode item;
}
[Serializable]
class BuildNode
{
public string build;
}
[Serializable]
public class ProductInfo
{
public string version;
public string versionSuffix;
[CanBeNull]
internal static ProductInfo GetProductInfo(string json)
{
try
{
var productInfo = JsonUtility.FromJson<ProductInfo>(json);
return productInfo;
}
catch (Exception)
{
Logger.Warn($"Failed to get version from json {json}");
}
return null;
}
}
// ReSharper disable once ClassNeverInstantiated.Global
[Serializable]
class ToolboxInstallData
{
// ReSharper disable once InconsistentNaming
public ActiveApplication active_application;
[CanBeNull]
public static string GetLatestBuildFromJson(string json)
{
try
{
#if UNITY_4_7 || UNITY_5_5
var toolbox = JsonConvert.DeserializeObject<ToolboxInstallData>(json);
#else
var toolbox = JsonUtility.FromJson<ToolboxInstallData>(json);
#endif
var builds = toolbox.active_application.builds;
if (builds != null && builds.Any())
return builds.First();
}
catch (Exception)
{
Logger.Warn($"Failed to get latest build from json {json}");
}
return null;
}
}
[Serializable]
class ActiveApplication
{
// ReSharper disable once InconsistentNaming
public List<string> builds;
}
#pragma warning restore 0649
public struct RiderInfo
{
public bool IsToolbox;
public string Presentation;
public Version BuildNumber;
public string BuildVersion; // added for backward compatibility
public ProductInfo ProductInfo;
public string Path;
public RiderInfo(string path, bool isToolbox)
{
BuildVersion = string.Empty;
if (path == RiderScriptEditor.CurrentEditor)
{
RiderScriptEditorData.instance.Init();
BuildNumber = RiderScriptEditorData.instance.editorBuildNumber;
ProductInfo = RiderScriptEditorData.instance.productInfo;
}
else
{
BuildNumber = GetBuildNumber(path);
ProductInfo = GetBuildVersion(path);
}
Path = new FileInfo(path).FullName; // normalize separators
var presentation = $"Rider {BuildNumber}";
if (ProductInfo != null && !string.IsNullOrEmpty(ProductInfo.version))
{
var suffix = string.IsNullOrEmpty(ProductInfo.versionSuffix) ? "" : $" {ProductInfo.versionSuffix}";
presentation = $"Rider {ProductInfo.version}{suffix}";
}
if (isToolbox)
presentation += " (JetBrains Toolbox)";
Presentation = presentation;
IsToolbox = isToolbox;
}
}
private static class Logger
{
internal static void Warn(string message, Exception e = null)
{
#if RIDER_EDITOR_PLUGIN // can't be used in com.unity.ide.rider
Log.GetLog(typeof(RiderPathLocator).Name).Warn(message);
if (e != null)
Log.GetLog(typeof(RiderPathLocator).Name).Warn(e);
#else
Debug.LogError(message);
if (e != null)
Debug.LogException(e);
#endif
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dab656c79e1985c40b31faebcda44442
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,139 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Debug = UnityEngine.Debug;
namespace Packages.Rider.Editor
{
public static class EditorPluginInterop
{
private static string EditorPluginAssemblyNamePrefix = "JetBrains.Rider.Unity.Editor.Plugin.";
public static readonly string EditorPluginAssemblyName = $"{EditorPluginAssemblyNamePrefix}Net46.Repacked";
public static readonly string EditorPluginAssemblyNameFallback = $"{EditorPluginAssemblyNamePrefix}Full.Repacked";
private static string ourEntryPointTypeName = "JetBrains.Rider.Unity.Editor.PluginEntryPoint";
private static Assembly ourEditorPluginAssembly;
public static Assembly EditorPluginAssembly
{
get
{
if (ourEditorPluginAssembly != null)
return ourEditorPluginAssembly;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
ourEditorPluginAssembly = assemblies.FirstOrDefault(a => a.GetName().Name.StartsWith(EditorPluginAssemblyNamePrefix));
return ourEditorPluginAssembly;
}
}
private static void DisableSyncSolutionOnceCallBack()
{
// RiderScriptableSingleton.Instance.CsprojProcessedOnce = true;
// Otherwise EditorPlugin regenerates all on every AppDomain reload
var assembly = EditorPluginAssembly;
if (assembly == null) return;
var type = assembly.GetType("JetBrains.Rider.Unity.Editor.Utils.RiderScriptableSingleton");
if (type == null) return;
var baseType = type.BaseType;
if (baseType == null) return;
var instance = baseType.GetProperty("Instance");
if (instance == null) return;
var instanceVal = instance.GetValue(null);
var member = type.GetProperty("CsprojProcessedOnce");
if (member==null) return;
member.SetValue(instanceVal, true);
}
public static string LogPath
{
get
{
try
{
var assembly = EditorPluginAssembly;
if (assembly == null) return null;
var type = assembly.GetType(ourEntryPointTypeName);
if (type == null) return null;
var field = type.GetField("LogPath", BindingFlags.NonPublic | BindingFlags.Static);
if (field == null) return null;
return field.GetValue(null) as string;
}
catch (Exception)
{
Debug.Log("Unable to do OpenFile to Rider from dll, fallback to com.unity.ide.rider implementation.");
}
return null;
}
}
public static bool OpenFileDllImplementation(string path, int line, int column)
{
var openResult = false;
// reflection for fast OpenFileLineCol, when Rider is started and protocol connection is established
try
{
var assembly = EditorPluginAssembly;
if (assembly == null) return false;
var type = assembly.GetType(ourEntryPointTypeName);
if (type == null) return false;
var field = type.GetField("OpenAssetHandler", BindingFlags.NonPublic | BindingFlags.Static);
if (field == null) return false;
var handlerInstance = field.GetValue(null);
var method = handlerInstance.GetType()
.GetMethod("OnOpenedAsset", new[] {typeof(string), typeof(int), typeof(int)});
if (method == null) return false;
var assetFilePath = path;
if (!string.IsNullOrEmpty(path))
assetFilePath = Path.GetFullPath(path);
openResult = (bool) method.Invoke(handlerInstance, new object[] {assetFilePath, line, column});
}
catch (Exception e)
{
Debug.Log("Unable to do OpenFile to Rider from dll, fallback to com.unity.ide.rider implementation.");
Debug.LogException(e);
}
return openResult;
}
public static bool EditorPluginIsLoadedFromAssets(Assembly assembly)
{
if (assembly == null)
return false;
var location = assembly.Location;
var currentDir = Directory.GetCurrentDirectory();
return location.StartsWith(currentDir, StringComparison.InvariantCultureIgnoreCase);
}
internal static void InitEntryPoint(Assembly assembly)
{
try
{
var version = RiderScriptEditorData.instance.editorBuildNumber;
if (version != null)
{
if (version.Major < 192)
DisableSyncSolutionOnceCallBack(); // is require for Rider prior to 2019.2
}
else
DisableSyncSolutionOnceCallBack();
var type = assembly.GetType("JetBrains.Rider.Unity.Editor.AfterUnity56.EntryPoint");
if (type == null)
type = assembly.GetType("JetBrains.Rider.Unity.Editor.UnitTesting.EntryPoint"); // oldRider
RuntimeHelpers.RunClassConstructor(type.TypeHandle);
}
catch (TypeInitializationException ex)
{
Debug.LogException(ex);
if (ex.InnerException != null)
Debug.LogException(ex.InnerException);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9bd02a3a916be64c9b47b1305149423
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
namespace Packages.Rider.Editor
{
public enum LoggingLevel
{
/// <summary>
/// Do not use it in logging. Only in config to disable logging.
/// </summary>
OFF,
/// <summary>For errors that lead to application failure</summary>
FATAL,
/// <summary>For errors that must be shown in Exception Browser</summary>
ERROR,
/// <summary>Suspicious situations but not errors</summary>
WARN,
/// <summary>Regular level for important events</summary>
INFO,
/// <summary>Additional info for debbuging</summary>
VERBOSE,
/// <summary>Methods &amp; callstacks tracing, more than verbose</summary>
TRACE,
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71bb46b59a9a7a346bbab1e185c723df
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,125 @@
using Unity.CodeEditor;
using UnityEditor;
using UnityEngine;
namespace Packages.Rider.Editor
{
public class PluginSettings
{
public static LoggingLevel SelectedLoggingLevel
{
get => (LoggingLevel) EditorPrefs.GetInt("Rider_SelectedLoggingLevel", 0);
set
{
EditorPrefs.SetInt("Rider_SelectedLoggingLevel", (int) value);
}
}
public static bool LogEventsCollectorEnabled
{
get { return EditorPrefs.GetBool("Rider_LogEventsCollectorEnabled", true); }
private set { EditorPrefs.SetBool("Rider_LogEventsCollectorEnabled", value); }
}
private static GUIStyle ourVersionInfoStyle = new GUIStyle()
{
normal = new GUIStyleState()
{
textColor = new Color(0, 0, 0, .6f),
},
margin = new RectOffset(4, 4, 4, 4),
};
/// <summary>
/// Preferences menu layout
/// </summary>
/// <remarks>
/// Contains all 3 toggles: Enable/Disable; Debug On/Off; Writing Launch File On/Off
/// </remarks>
[SettingsProvider]
private static SettingsProvider RiderPreferencesItem()
{
if (!RiderScriptEditor.IsRiderInstallation(RiderScriptEditor.CurrentEditor))
return null;
if (!RiderScriptEditorData.instance.shouldLoadEditorPlugin)
return null;
var provider = new SettingsProvider("Preferences/Rider", SettingsScope.User)
{
label = "Rider",
keywords = new[] { "Rider" },
guiHandler = (searchContext) =>
{
EditorGUIUtility.labelWidth = 200f;
EditorGUILayout.BeginVertical();
GUILayout.BeginVertical();
LogEventsCollectorEnabled =
EditorGUILayout.Toggle(new GUIContent("Pass Console to Rider:"), LogEventsCollectorEnabled);
GUILayout.EndVertical();
GUILayout.Label("");
if (!string.IsNullOrEmpty(EditorPluginInterop.LogPath))
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Log file:");
var previous = GUI.enabled;
GUI.enabled = previous && SelectedLoggingLevel != LoggingLevel.OFF;
var button = GUILayout.Button(new GUIContent("Open log"));
if (button)
{
//UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(PluginEntryPoint.LogPath, 0);
// works much faster than the commented code, when Rider is already started
CodeEditor.CurrentEditor.OpenProject(EditorPluginInterop.LogPath, 0, 0);
}
GUI.enabled = previous;
GUILayout.EndHorizontal();
}
var loggingMsg =
@"Sets the amount of Rider Debug output. If you are about to report an issue, please select Verbose logging level and attach Unity console output to the issue.";
SelectedLoggingLevel =
(LoggingLevel) EditorGUILayout.EnumPopup(new GUIContent("Logging Level:", loggingMsg),
SelectedLoggingLevel);
EditorGUILayout.HelpBox(loggingMsg, MessageType.None);
LinkButton("https://github.com/JetBrains/resharper-unity");
GUILayout.FlexibleSpace();
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
var assembly = EditorPluginInterop.EditorPluginAssembly;
if (assembly != null)
{
var version = assembly.GetName().Version;
GUILayout.Label("Plugin version: " + version, ourVersionInfoStyle);
}
GUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
};
return provider;
}
private static void LinkButton(string url)
{
var style = EditorStyles.linkLabel;
var bClicked = GUILayout.Button(url, style);
var rect = GUILayoutUtility.GetLastRect();
rect.width = style.CalcSize(new GUIContent(url)).x;
EditorGUIUtility.AddCursorRect(rect, MouseCursor.Link);
if (bClicked)
Application.OpenURL(url);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1bfe12aa306c0c74db4f4f1a1a0ae5ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aa290bd9a165a0543a4bf85ac73914bc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,16 @@
using Unity.CodeEditor;
using UnityEditor;
namespace Packages.Rider.Editor.PostProcessors
{
public class RiderAssetPostprocessor: AssetPostprocessor
{
public static bool OnPreGeneratingCSProjectFiles()
{
var path = RiderScriptEditor.GetEditorRealPath(CodeEditor.CurrentEditorInstallation);
if (RiderScriptEditor.IsRiderInstallation(path))
return !ProjectGeneration.isRiderProjectGeneration;
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 45471ad7b8c1f964da5e3c07d57fbf4f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 313cbe17019f1934397f91069831062c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,22 @@
using System.IO;
using System.Text;
namespace Packages.Rider.Editor {
class FileIOProvider : IFileIO
{
public bool Exists(string fileName)
{
return File.Exists(fileName);
}
public string ReadAllText(string fileName)
{
return File.ReadAllText(fileName);
}
public void WriteAllText(string fileName, string content)
{
File.WriteAllText(fileName, content, Encoding.UTF8);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a6ba838b1348d5e46a7eaacd1646c1d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,14 @@
namespace Packages.Rider.Editor {
class GUIDProvider : IGUIDGenerator
{
public string ProjectGuid(string projectName, string assemblyName)
{
return SolutionGuidGenerator.GuidForProject(projectName + assemblyName);
}
public string SolutionGuid(string projectName, string extension)
{
return SolutionGuidGenerator.GuidForSolution(projectName, extension); // GetExtensionOfSourceFiles(assembly.sourceFiles)
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8cfde1a59fb35574189691a9de1df93b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7078f19173ceac84fb9e29b9f6175201
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,44 @@
using System;
using System.IO;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Packages.Rider.Editor
{
internal class RiderInitializer
{
public void Initialize(string editorPath)
{
var assembly = EditorPluginInterop.EditorPluginAssembly;
if (EditorPluginInterop.EditorPluginIsLoadedFromAssets(assembly))
{
Debug.LogError($"Please delete {assembly.Location}. Unity 2019.2+ loads it directly from Rider installation. To disable this, open Rider's settings, search and uncheck 'Automatically install and update Rider's Unity editor plugin'.");
return;
}
var relPath = "../../plugins/rider-unity/EditorPlugin";
if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
relPath = "Contents/plugins/rider-unity/EditorPlugin";
var baseDir = Path.Combine(editorPath, relPath);
var dllFile = new FileInfo(Path.Combine(baseDir, $"{EditorPluginInterop.EditorPluginAssemblyName}.dll"));
if (!dllFile.Exists)
dllFile = new FileInfo(Path.Combine(baseDir, $"{EditorPluginInterop.EditorPluginAssemblyNameFallback}.dll"));
if (dllFile.Exists)
{
var bytes = File.ReadAllBytes(dllFile.FullName);
assembly = AppDomain.CurrentDomain.Load(bytes); // doesn't lock assembly on disk
// assembly = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(dllFile.FullName)); // use this for external source debug
if (PluginSettings.SelectedLoggingLevel >= LoggingLevel.TRACE)
Debug.Log($"Rider EditorPluging loaded from {dllFile.FullName}");
EditorPluginInterop.InitEntryPoint(assembly);
}
else
{
Debug.Log($"Unable to find Rider EditorPlugin {dllFile.FullName} for Unity ");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5a0cc9645f0e2d4fb816156dcf3f4dd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,426 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Packages.Rider.Editor.Util;
using Unity.CodeEditor;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Packages.Rider.Editor
{
[InitializeOnLoad]
public class RiderScriptEditor : IExternalCodeEditor
{
IDiscovery m_Discoverability;
IGenerator m_ProjectGeneration;
RiderInitializer m_Initiliazer = new RiderInitializer();
static RiderScriptEditor()
{
try
{
var projectGeneration = new ProjectGeneration();
var editor = new RiderScriptEditor(new Discovery(), projectGeneration);
CodeEditor.Register(editor);
var path = GetEditorRealPath(CurrentEditor);
if (IsRiderInstallation(path))
{
RiderPathLocator.RiderInfo[] installations = null;
if (!RiderScriptEditorData.instance.initializedOnce)
{
installations = RiderPathLocator.GetAllRiderPaths().OrderBy(a=>a.BuildNumber).ToArray();
// is likely outdated
if (installations.Any() && installations.All(a => GetEditorRealPath(a.Path) != path))
{
if (RiderPathLocator.IsToolbox(path)) // is toolbox - update
{
var toolboxInstallations = installations.Where(a => a.IsToolbox).ToArray();
if (toolboxInstallations.Any())
{
var newEditor = toolboxInstallations.Last().Path;
CodeEditor.SetExternalScriptEditor(newEditor);
path = newEditor;
}
else
{
var newEditor = installations.Last().Path;
CodeEditor.SetExternalScriptEditor(newEditor);
path = newEditor;
}
}
else // is non toolbox - notify
{
var newEditorName = installations.Last().Presentation;
Debug.LogWarning($"Consider updating External Editor in Unity to Rider {newEditorName}.");
}
}
ShowWarningOnUnexpectedScriptEditor(path);
RiderScriptEditorData.instance.initializedOnce = true;
}
if (!FileSystemUtil.EditorPathExists(path)) // previously used rider was removed
{
if (installations == null)
installations = RiderPathLocator.GetAllRiderPaths().OrderBy(a=>a.BuildNumber).ToArray();
if (installations.Any())
{
var newEditor = installations.Last().Path;
CodeEditor.SetExternalScriptEditor(newEditor);
path = newEditor;
}
}
RiderScriptEditorData.instance.Init();
editor.CreateSolutionIfDoesntExist();
if (RiderScriptEditorData.instance.shouldLoadEditorPlugin)
{
editor.m_Initiliazer.Initialize(path);
}
InitProjectFilesWatcher();
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
private static void ShowWarningOnUnexpectedScriptEditor(string path)
{
// Show warning, when Unity was started from Rider, but external editor is different https://github.com/JetBrains/resharper-unity/issues/1127
try
{
var args = Environment.GetCommandLineArgs();
var commandlineParser = new CommandLineParser(args);
if (commandlineParser.Options.ContainsKey("-riderPath"))
{
var originRiderPath = commandlineParser.Options["-riderPath"];
var originRealPath = GetEditorRealPath(originRiderPath);
var originVersion = RiderPathLocator.GetBuildNumber(originRealPath);
var version = RiderPathLocator.GetBuildNumber(path);
if (originVersion != null && originVersion != version)
{
Debug.LogWarning("Unity was started by a version of Rider that is not the current default external editor. Advanced integration features cannot be enabled.");
Debug.Log($"Unity was started by Rider {originVersion}, but external editor is set to: {path}");
}
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
private static void InitProjectFilesWatcher()
{
var watcher = new FileSystemWatcher();
watcher.Path = Directory.GetCurrentDirectory();
watcher.NotifyFilter = NotifyFilters.LastWrite; //Watch for changes in LastWrite times
watcher.Filter = "*.*";
// Add event handlers.
watcher.Changed += OnChanged;
watcher.Created += OnChanged;
watcher.EnableRaisingEvents = true; // Begin watching.
AppDomain.CurrentDomain.DomainUnload += (EventHandler) ((_, __) =>
{
watcher.Dispose();
});
}
private static void OnChanged(object sender, FileSystemEventArgs e)
{
var extension = Path.GetExtension(e.FullPath);
if (extension == ".sln" || extension == ".csproj")
RiderScriptEditorData.instance.hasChanges = true;
}
internal static string GetEditorRealPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return path;
}
if (!FileSystemUtil.EditorPathExists(path))
return path;
if (SystemInfo.operatingSystemFamily != OperatingSystemFamily.Windows)
{
var realPath = FileSystemUtil.GetFinalPathName(path);
// case of snap installation
if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.Linux)
{
if (new FileInfo(path).Name.ToLowerInvariant() == "rider" &&
new FileInfo(realPath).Name.ToLowerInvariant() == "snap")
{
var snapInstallPath = "/snap/rider/current/bin/rider.sh";
if (new FileInfo(snapInstallPath).Exists)
return snapInstallPath;
}
}
// in case of symlink
return realPath;
}
return path;
}
const string unity_generate_all = "unity_generate_all_csproj";
const string unity_generate_player_projects = "unity_generate_player_projects";
public RiderScriptEditor(IDiscovery discovery, IGenerator projectGeneration)
{
m_Discoverability = discovery;
m_ProjectGeneration = projectGeneration;
}
private static string[] defaultExtensions
{
get
{
var customExtensions = new[] {"json", "asmdef", "log", "xaml"};
return EditorSettings.projectGenerationBuiltinExtensions.Concat(EditorSettings.projectGenerationUserExtensions)
.Concat(customExtensions).Distinct().ToArray();
}
}
private static string[] HandledExtensions
{
get
{
return HandledExtensionsString.Split(new[] {';'}, StringSplitOptions.RemoveEmptyEntries).Select(s => s.TrimStart('.', '*'))
.ToArray();
}
}
private static string HandledExtensionsString
{
get { return EditorPrefs.GetString("Rider_UserExtensions", string.Join(";", defaultExtensions));}
set { EditorPrefs.SetString("Rider_UserExtensions", value); }
}
private static bool SupportsExtension(string path)
{
var extension = Path.GetExtension(path);
if (string.IsNullOrEmpty(extension))
return false;
return HandledExtensions.Contains(extension.TrimStart('.'));
}
public void OnGUI()
{
if (RiderScriptEditorData.instance.shouldLoadEditorPlugin)
{
HandledExtensionsString = EditorGUILayout.TextField(new GUIContent("Extensions handled: "), HandledExtensionsString);
}
EditorGUILayout.LabelField("Generate .csproj files for:");
EditorGUI.indentLevel++;
m_ProjectGeneration.GenerateAll(SettingsButton(unity_generate_all, "Internal packages", "Generate csproj files for all packages, including packages marked as internal."));
m_ProjectGeneration.AssemblyNameProvider.GeneratePlayerProjects(SettingsButton(unity_generate_player_projects, "Player projects", "For each player project generate an additional csproj with the name 'project-player.csproj'."));
EditorGUI.indentLevel--;
}
static bool SettingsButton(string preference, string guiMessage, string toolTip)
{
var prevValue = EditorPrefs.GetBool(preference, false);
;
var newValue = EditorGUILayout.Toggle(new GUIContent(guiMessage, toolTip), prevValue);
if (newValue != prevValue)
{
EditorPrefs.SetBool(preference, newValue);
}
return newValue;
}
public void SyncIfNeeded(string[] addedFiles, string[] deletedFiles, string[] movedFiles, string[] movedFromFiles,
string[] importedFiles)
{
m_ProjectGeneration.SyncIfNeeded(addedFiles.Union(deletedFiles).Union(movedFiles).Union(movedFromFiles),
importedFiles);
}
public void SyncAll()
{
AssetDatabase.Refresh();
if (RiderScriptEditorData.instance.hasChanges)
{
m_ProjectGeneration.Sync();
RiderScriptEditorData.instance.hasChanges = false;
}
}
public void Initialize(string editorInstallationPath) // is called each time ExternalEditor is changed
{
RiderScriptEditorData.instance.Invalidate(editorInstallationPath);
m_ProjectGeneration.Sync(); // regenerate csproj and sln for new editor
}
public bool OpenProject(string path, int line, int column)
{
if (path != "" && !SupportsExtension(path)) // Assets - Open C# Project passes empty path here
{
return false;
}
if (path == "" && SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
{
// there is a bug in DllImplementation - use package implementation here instead https://github.cds.internal.unity3d.com/unity/com.unity.ide.rider/issues/21
return OpenOSXApp(path, line, column);
}
if (!IsUnityScript(path))
{
var fastOpenResult = EditorPluginInterop.OpenFileDllImplementation(path, line, column);
if (fastOpenResult)
return true;
}
if (SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX)
{
return OpenOSXApp(path, line, column);
}
var solution = GetSolutionFile(path); // TODO: If solution file doesn't exist resync.
solution = solution == "" ? "" : $"\"{solution}\"";
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = CodeEditor.CurrentEditorInstallation,
Arguments = $"{solution} -l {line} \"{path}\"",
UseShellExecute = true,
}
};
process.Start();
return true;
}
private bool OpenOSXApp(string path, int line, int column)
{
var solution = GetSolutionFile(path); // TODO: If solution file doesn't exist resync.
solution = solution == "" ? "" : $"\"{solution}\"";
var pathArguments = path == "" ? "" : $"-l {line} \"{path}\"";
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "open",
Arguments = $"-n \"{CodeEditor.CurrentEditorInstallation}\" --args {solution} {pathArguments}",
CreateNoWindow = true,
UseShellExecute = true,
}
};
process.Start();
return true;
}
private string GetSolutionFile(string path)
{
if (IsUnityScript(path))
{
return Path.Combine(GetBaseUnityDeveloperFolder(), "Projects/CSharp/Unity.CSharpProjects.gen.sln");
}
var solutionFile = m_ProjectGeneration.SolutionFile();
if (File.Exists(solutionFile))
{
return solutionFile;
}
return "";
}
static bool IsUnityScript(string path)
{
if (UnityEditor.Unsupported.IsDeveloperBuild())
{
var baseFolder = GetBaseUnityDeveloperFolder().Replace("\\", "/");
var lowerPath = path.ToLowerInvariant().Replace("\\", "/");
if (lowerPath.Contains((baseFolder + "/Runtime").ToLowerInvariant())
|| lowerPath.Contains((baseFolder + "/Editor").ToLowerInvariant()))
{
return true;
}
}
return false;
}
static string GetBaseUnityDeveloperFolder()
{
return Directory.GetParent(EditorApplication.applicationPath).Parent.Parent.FullName;
}
public bool TryGetInstallationForPath(string editorPath, out CodeEditor.Installation installation)
{
if (FileSystemUtil.EditorPathExists(editorPath) && IsRiderInstallation(editorPath))
{
var info = new RiderPathLocator.RiderInfo(editorPath, false);
installation = new CodeEditor.Installation
{
Name = info.Presentation,
Path = info.Path
};
return true;
}
installation = default;
return false;
}
public static bool IsRiderInstallation(string path)
{
if (IsAssetImportWorkerProcess())
return false;
if (string.IsNullOrEmpty(path))
{
return false;
}
var fileInfo = new FileInfo(path);
var filename = fileInfo.Name.ToLowerInvariant();
return filename.StartsWith("rider", StringComparison.Ordinal);
}
private static bool IsAssetImportWorkerProcess()
{
#if UNITY_2019_3_OR_NEWER
return UnityEditor.Experimental.AssetDatabaseExperimental.IsAssetImportWorkerProcess();
#else
return false;
#endif
}
public static string CurrentEditor // works fast, doesn't validate if executable really exists
=> EditorPrefs.GetString("kScriptsDefaultApp");
public CodeEditor.Installation[] Installations => m_Discoverability.PathCallback();
public void CreateSolutionIfDoesntExist()
{
if (!m_ProjectGeneration.HasSolutionBeenGenerated())
{
m_ProjectGeneration.Sync();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c4095d72f77fbb64ea39b8b3ca246622
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,33 @@
using System;
using UnityEditor;
using UnityEngine;
namespace Packages.Rider.Editor
{
public class RiderScriptEditorData : ScriptableSingleton<RiderScriptEditorData>
{
[SerializeField] internal bool hasChanges = true; // sln/csproj files were changed
[SerializeField] internal bool shouldLoadEditorPlugin;
[SerializeField] internal bool initializedOnce;
[SerializeField] internal Version editorBuildNumber;
[SerializeField] internal RiderPathLocator.ProductInfo productInfo;
public void Init()
{
if (editorBuildNumber == null)
{
Invalidate(RiderScriptEditor.CurrentEditor);
}
}
public void Invalidate(string editorInstallationPath)
{
editorBuildNumber = RiderPathLocator.GetBuildNumber(editorInstallationPath);
productInfo = RiderPathLocator.GetBuildVersion(editorInstallationPath);
if (editorBuildNumber == null)
shouldLoadEditorPlugin = false;
shouldLoadEditorPlugin = editorBuildNumber >= new Version("191.7141.156");
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f079e3afd077fb94fa2bda74d6409499
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a52391bc44c477f40a547ed4ef3b9560
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEditor;
namespace Packages.Rider.Editor.UnitTesting
{
public class CallbackData : ScriptableSingleton<CallbackData>
{
public bool isRider;
[UsedImplicitly] public static event EventHandler Changed = (sender, args) => { };
internal void RaiseChangedEvent()
{
Changed(null, EventArgs.Empty);
}
public List<TestEvent> events = new List<TestEvent>();
[UsedImplicitly]
public void Clear()
{
events.Clear();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 010246a07de7cb34185a2a7b1c1fad59
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,18 @@
#if TEST_FRAMEWORK
using UnityEditor;
using UnityEditor.TestTools.TestRunner.Api;
using UnityEngine;
namespace Packages.Rider.Editor.UnitTesting
{
[InitializeOnLoad]
internal static class CallbackInitializer
{
static CallbackInitializer()
{
if (CallbackData.instance.isRider)
ScriptableObject.CreateInstance<TestRunnerApi>().RegisterCallbacks(ScriptableObject.CreateInstance<TestsCallback>(), 0);
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aa1c6b1a353ab464782fc1e7c051eb02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,65 @@
using JetBrains.Annotations;
using UnityEngine;
#if TEST_FRAMEWORK
using UnityEditor;
using UnityEditor.TestTools.TestRunner.Api;
#else
using System;
#endif
namespace Packages.Rider.Editor.UnitTesting
{
public static class RiderTestRunner
{
#if TEST_FRAMEWORK
private static readonly TestsCallback Callback = ScriptableObject.CreateInstance<TestsCallback>();
#endif
[UsedImplicitly]
public static void RunTestsWithSyncCallbacks(string sessionId, int testMode, string[] assemblyNames,
string[] testNames, string[] categoryNames, string[] groupNames, int? buildTarget,
string callbacksHandlerCodeBase, string callbacksHandlerTypeName, string[] callbacksHandlerDependencies)
{
#if !TEST_FRAMEWORK
Debug.LogError("Update Test Framework package to v.1.1.1+ to run tests from Rider.");
throw new NotSupportedException("Incompatible `Test Framework` package in Unity. Update to v.1.1.1+");
#else
SyncTestRunEventsHandler.instance.InitRun(sessionId, callbacksHandlerCodeBase, callbacksHandlerTypeName, callbacksHandlerDependencies);
RunTests(testMode, assemblyNames, testNames, categoryNames, groupNames, buildTarget);
#endif
}
[UsedImplicitly]
public static void RunTests(int testMode, string[] assemblyNames, string[] testNames, string[] categoryNames, string[] groupNames, int? buildTarget)
{
#if !TEST_FRAMEWORK
Debug.LogError("Update Test Framework package to v.1.1.1+ to run tests from Rider.");
throw new NotSupportedException("Incompatible `Test Framework` package in Unity. Update to v.1.1.1+");
#else
CallbackData.instance.isRider = true;
var api = ScriptableObject.CreateInstance<TestRunnerApi>();
var settings = new ExecutionSettings();
var filter = new Filter
{
assemblyNames = assemblyNames,
testNames = testNames,
categoryNames = categoryNames,
groupNames = groupNames,
targetPlatform = (BuildTarget?) buildTarget
};
if (testMode > 0) // for future use - test-framework would allow running both Edit and Play test at once
filter.testMode = (TestMode) testMode;
settings.filters = new []{
filter
};
api.Execute(settings);
api.UnregisterCallbacks(Callback); // avoid multiple registrations
api.RegisterCallbacks(Callback); // This can be used to receive information about when the test suite and individual tests starts and stops. Provide this with a scriptable object implementing ICallbacks
#endif
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c3b27069cb3ddf42ba1260eeefcdd1c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,34 @@
#if TEST_FRAMEWORK
using NUnit.Framework.Interfaces;
using Packages.Rider.Editor.UnitTesting;
using UnityEngine.TestRunner;
[assembly: TestRunCallback(typeof(SyncTestRunCallback))]
namespace Packages.Rider.Editor.UnitTesting
{
public class SyncTestRunCallback : ITestRunCallback
{
public void RunStarted(ITest testsToRun)
{
}
public void RunFinished(ITestResult testResults)
{
SyncTestRunEventsHandler.instance.OnRunFinished();
}
public void TestStarted(ITest test)
{
if (!test.IsSuite)
SyncTestRunEventsHandler.instance.OnTestStarted(test.FullName);
}
public void TestFinished(ITestResult result)
{
if (!result.Test.IsSuite)
SyncTestRunEventsHandler.instance.OnTestFinished();
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58ab3828fb407c742a48b82bc5983a87
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,148 @@
#if TEST_FRAMEWORK
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace Packages.Rider.Editor.UnitTesting
{
internal class SyncTestRunEventsHandler : ScriptableSingleton<SyncTestRunEventsHandler>
{
[SerializeField] private string m_SessionId;
[SerializeField] private string m_HandlerCodeBase;
[SerializeField] private string m_HandlerTypeName;
[SerializeField] private string[] m_HandlerDependencies;
[SerializeField] private bool m_RunInitialized;
private object m_Handler;
private MethodInfo m_OnSessionStartedMethodInfo;
private MethodInfo m_OnTestStartedMethodInfo;
private MethodInfo m_OnTestFinishedMethodInfo;
private MethodInfo m_OnSessionFinishedMethodInfo;
internal void InitRun(string sessionId, string handlerCodeBase, string handlerTypeName, string[] handlerDependencies)
{
if (PluginSettings.SelectedLoggingLevel >= LoggingLevel.TRACE)
Debug.Log("Rider Test Runner: initializing sync callbacks handler: " +
$"sessionId={sessionId}, " +
$"codeBase={handlerCodeBase}, " +
$"typeName={handlerTypeName}, " +
$"dependencies={(handlerDependencies == null ? "" : string.Join("; ", handlerDependencies))}");
m_SessionId = sessionId;
m_HandlerCodeBase = handlerCodeBase;
m_HandlerTypeName = handlerTypeName;
m_HandlerDependencies = handlerDependencies;
m_RunInitialized = true;
CreateHandlerInstance();
SafeInvokeHandlerMethod(m_OnSessionStartedMethodInfo, Array.Empty<object>());
}
private void OnEnable()
{
if (m_RunInitialized)
CreateHandlerInstance();
}
internal void OnTestStarted(string testId)
{
if (m_RunInitialized)
SafeInvokeHandlerMethod(m_OnTestStartedMethodInfo, new object[] {testId});
}
internal void OnTestFinished()
{
if (m_RunInitialized)
SafeInvokeHandlerMethod(m_OnTestFinishedMethodInfo, Array.Empty<object>());
}
internal void OnRunFinished()
{
if (!m_RunInitialized)
return;
SafeInvokeHandlerMethod(m_OnSessionFinishedMethodInfo, Array.Empty<object>());
CleanUp();
m_RunInitialized = false;
}
private void SafeInvokeHandlerMethod(MethodInfo methodInfo, object[] args)
{
try
{
methodInfo?.Invoke(m_Handler, args);
}
catch (Exception e)
{
Debug.LogException(e);
}
}
private void CreateHandlerInstance()
{
try
{
if (m_HandlerDependencies != null)
foreach (var dependency in m_HandlerDependencies)
{
if (PluginSettings.SelectedLoggingLevel >= LoggingLevel.TRACE)
Debug.Log($"Rider Test Runner: loading assembly from {dependency}");
Assembly.LoadFrom(dependency);
}
if (PluginSettings.SelectedLoggingLevel >= LoggingLevel.TRACE)
Debug.Log($"Rider Test Runner: loading assembly from {m_HandlerCodeBase}");
var assembly = Assembly.LoadFrom(m_HandlerCodeBase);
var type = assembly.GetType(m_HandlerTypeName);
if (type == null)
{
Debug.LogError($"Rider Test Runner: type '{m_HandlerTypeName}' not found in assembly '{assembly.FullName}'");
return;
}
if (PluginSettings.SelectedLoggingLevel >= LoggingLevel.TRACE)
Debug.Log($"Rider Test Runner: creating instance of type '{type.AssemblyQualifiedName}'");
m_Handler = Activator.CreateInstance(type, m_SessionId);
m_OnSessionStartedMethodInfo = type.GetMethod("OnSessionStarted", BindingFlags.Instance | BindingFlags.Public);
if (m_OnSessionStartedMethodInfo == null)
{
Debug.LogError($"Rider Test Runner: OnSessionStarted method not found in type='{type.AssemblyQualifiedName}'");
return;
}
m_OnTestStartedMethodInfo = type.GetMethod("OnTestStarted", BindingFlags.Instance | BindingFlags.Public);
if (m_OnTestStartedMethodInfo == null)
{
Debug.LogError($"Rider Test Runner: OnTestStarted method not found in type='{type.AssemblyQualifiedName}'");
return;
}
m_OnTestFinishedMethodInfo = type.GetMethod("OnTestFinished", BindingFlags.Instance | BindingFlags.Public);
if (m_OnTestFinishedMethodInfo == null)
{
Debug.LogError($"Rider Test Runner: OnTestFinished method not found in type='{type.AssemblyQualifiedName}'");
return;
}
m_OnSessionFinishedMethodInfo = type.GetMethod("OnSessionFinished", BindingFlags.Instance | BindingFlags.Public);
if (m_OnSessionFinishedMethodInfo == null)
Debug.LogError($"Rider Test Runner: OnSessionFinished method not found in type='{type.AssemblyQualifiedName}'");
}
catch (Exception e)
{
Debug.LogException(e);
}
}
private void CleanUp()
{
m_Handler = null;
m_OnSessionStartedMethodInfo = null;
m_OnSessionFinishedMethodInfo = null;
m_OnTestStartedMethodInfo = null;
m_OnTestFinishedMethodInfo = null;
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 48483563a64de3a4e8690122762055f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using System;
using NUnit.Framework.Interfaces;
namespace Packages.Rider.Editor.UnitTesting
{
[Serializable]
public enum EventType { TestStarted, TestFinished, RunFinished, RunStarted } // do not reorder
[Serializable]
public class TestEvent
{
public EventType type;
public string id;
public string assemblyName;
public string output;
public TestStatus testStatus;
public double duration;
public string parentId;
public TestEvent(EventType type, string id, string assemblyName, string output, double duration, TestStatus testStatus, string parentID)
{
this.type = type;
this.id = id;
this.assemblyName = assemblyName;
this.output = output;
this.testStatus = testStatus;
this.duration = duration;
parentId = parentID;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f9413c47b3a14a64e8810ce76d1a6032
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,95 @@
#if TEST_FRAMEWORK
using System;
using System.Text;
using UnityEditor.TestTools.TestRunner.Api;
using UnityEngine;
namespace Packages.Rider.Editor.UnitTesting
{
public class TestsCallback : ScriptableObject, IErrorCallbacks
{
public void RunFinished(ITestResultAdaptor result)
{
CallbackData.instance.isRider = false;
CallbackData.instance.events.Add(
new TestEvent(EventType.RunFinished, "", "","", 0, ParseTestStatus(result.TestStatus), ""));
CallbackData.instance.RaiseChangedEvent();
}
public void RunStarted(ITestAdaptor testsToRun)
{
CallbackData.instance.events.Add(
new TestEvent(EventType.RunStarted, "", "","", 0, NUnit.Framework.Interfaces.TestStatus.Passed, ""));
CallbackData.instance.RaiseChangedEvent();
}
public void TestStarted(ITestAdaptor result)
{
if (result.Method == null) return;
CallbackData.instance.events.Add(
new TestEvent(EventType.TestStarted, GetUniqueName(result), result.Method.TypeInfo.Assembly.GetName().Name, "", 0, NUnit.Framework.Interfaces.TestStatus.Passed, result.ParentFullName));
CallbackData.instance.RaiseChangedEvent();
}
public void TestFinished(ITestResultAdaptor result)
{
if (result.Test.Method == null) return;
CallbackData.instance.events.Add(
new TestEvent(EventType.TestFinished, GetUniqueName(result.Test), result.Test.Method.TypeInfo.Assembly.GetName().Name, ExtractOutput(result), result.Duration, ParseTestStatus(result.TestStatus), result.Test.ParentFullName));
CallbackData.instance.RaiseChangedEvent();
}
public void OnError(string message)
{
CallbackData.instance.isRider = false;
CallbackData.instance.events.Add(
new TestEvent(EventType.RunFinished, "", "",message, 0, NUnit.Framework.Interfaces.TestStatus.Failed, ""));
CallbackData.instance.RaiseChangedEvent();
}
// todo: reimplement JetBrains.Rider.Unity.Editor.AfterUnity56.UnitTesting.TestEventsSender.GetUniqueName
private static string GetUniqueName(ITestAdaptor test)
{
string str = test.FullName;
return str;
}
private static NUnit.Framework.Interfaces.TestStatus ParseTestStatus(TestStatus testStatus)
{
return (NUnit.Framework.Interfaces.TestStatus)Enum.Parse(typeof(NUnit.Framework.Interfaces.TestStatus), testStatus.ToString());
}
private static string ExtractOutput(ITestResultAdaptor testResult)
{
var stringBuilder = new StringBuilder();
if (testResult.Message != null)
{
stringBuilder.AppendLine("Message: ");
stringBuilder.AppendLine(testResult.Message);
}
if (!string.IsNullOrEmpty(testResult.Output))
{
stringBuilder.AppendLine("Output: ");
stringBuilder.AppendLine(testResult.Output);
}
if (!string.IsNullOrEmpty(testResult.StackTrace))
{
stringBuilder.AppendLine("Stacktrace: ");
stringBuilder.AppendLine(testResult.StackTrace);
}
var result = stringBuilder.ToString();
if (result.Length > 0)
return result;
return testResult.Output ?? string.Empty;
}
}
}
#endif

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58aa570dbe0761f43b25ff6c2265bbe2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5e726086cd652f82087d59d67d2c24cd
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,36 @@
using System.Collections.Generic;
namespace Packages.Rider.Editor.Util
{
public class CommandLineParser
{
public Dictionary<string, string> Options = new Dictionary<string, string>();
public CommandLineParser(string[] args)
{
var i = 0;
while (i < args.Length)
{
var arg = args[i];
if (!arg.StartsWith("-"))
{
i++;
continue;
}
string value = null;
if (i + 1 < args.Length && !args[i + 1].StartsWith("-"))
{
value = args[i + 1];
i++;
}
if (!(Options.ContainsKey(arg)))
{
Options.Add(arg, value);
}
i++;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 154ace4bd16de9f4e84052ac257786d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,66 @@
using System;
using System.ComponentModel;
using System.IO;
using System.Text;
using JetBrains.Annotations;
using UnityEngine;
namespace Packages.Rider.Editor.Util
{
public static class FileSystemUtil
{
[NotNull]
public static string GetFinalPathName([NotNull] string path)
{
if (path == null) throw new ArgumentNullException("path");
// up to MAX_PATH. MAX_PATH on Linux currently 4096, on Mac OS X 1024
// doc: http://man7.org/linux/man-pages/man3/realpath.3.html
var sb = new StringBuilder(8192);
var result = LibcNativeInterop.realpath(path, sb);
if (result == IntPtr.Zero)
{
throw new Win32Exception($"{path} was not resolved.");
}
return new FileInfo(sb.ToString()).FullName;
}
public static string FileNameWithoutExtension(string path)
{
if (string.IsNullOrEmpty(path))
{
return "";
}
var indexOfDot = -1;
var indexOfSlash = 0;
for (var i = path.Length - 1; i >= 0; i--)
{
if (indexOfDot == -1 && path[i] == '.')
{
indexOfDot = i;
}
if (indexOfSlash == 0 && path[i] == '/' || path[i] == '\\')
{
indexOfSlash = i + 1;
break;
}
}
if (indexOfDot == -1)
{
indexOfDot = path.Length;
}
return path.Substring(indexOfSlash, indexOfDot - indexOfSlash);
}
public static bool EditorPathExists(string editorPath)
{
return SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX && new DirectoryInfo(editorPath).Exists
|| SystemInfo.operatingSystemFamily != OperatingSystemFamily.MacOSX && new FileInfo(editorPath).Exists;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bdbd564a9fdad0b738e76d030cad1204
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,12 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Packages.Rider.Editor.Util
{
internal static class LibcNativeInterop
{
[DllImport("libc", SetLastError = true)]
public static extern IntPtr realpath(string path, StringBuilder resolved_path);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 071c17858dc6c47ada7b2a1f1ded5402
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,25 @@
using JetBrains.Annotations;
using Packages.Rider.Editor;
using Unity.CodeEditor;
// Is called via commandline from Rider Notification after checking out from source control.
// ReSharper disable once CheckNamespace
namespace JetBrains.Rider.Unity.Editor
{
public static class RiderMenu
{
[UsedImplicitly]
public static void MenuOpenProject()
{
if (RiderScriptEditor.IsRiderInstallation(RiderScriptEditor.CurrentEditor))
{
// Force the project files to be sync
CodeEditor.CurrentEditor.SyncAll();
// Load Project
CodeEditor.CurrentEditor.OpenProject();
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8860c53ca4073d4f92c403e709c12ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,20 @@
using System;
using System.Linq;
using UnityEngine;
namespace Packages.Rider.Editor.Util
{
public static class UnityUtils
{
internal static readonly string UnityApplicationVersion = Application.unityVersion;
public static Version UnityVersion
{
get
{
var ver = UnityApplicationVersion.Split(".".ToCharArray()).Take(2).Aggregate((a, b) => a + "." + b);
return new Version(ver);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3ec9edad2de6c4df3a146b543a0fbc4c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,23 @@
{
"name": "Unity.Rider.Editor",
"references": [
"GUID:0acc523941302664db1f4e527237feb3",
"GUID:27619889b8ba8c24980f49ee34dbb44a"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.test-framework",
"expression": "1.1.1",
"define": "TEST_FRAMEWORK"
}
]
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d528c8c98d269ca44a06cd9624a03945
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,19 @@
{
"name": "com.unity.ide.rider",
"displayName": "JetBrains Rider Editor",
"description": "Code editor integration for supporting JetBrains Rider as code editor for unity. Adds support for generating csproj files for code completion, auto discovery of installations, etc.",
"version": "1.2.1",
"unity": "2019.2",
"unityRelease": "0a12",
"dependencies": {
"com.unity.test-framework": "1.1.1"
},
"relatedPackages": {
"com.unity.ide.rider.tests": "1.2.1"
},
"repository": {
"type": "git",
"url": "git@github.cds.internal.unity3d.com:unity/com.unity.ide.rider.git",
"revision": "038f7161a30add43d98b31fd686ade12ffb96adb"
}
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 66c95bb3c74257f41bae2622511dc02d
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: