You're breathtaking!
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface;
|
||||
using FlaxEngine;
|
||||
using MethodInfo = System.Reflection.MethodInfo;
|
||||
|
||||
namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached collection of custom nodes types including cached node archetypes for each one of them.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Modules.SourceCodeEditing.CachedTypesCollection" />
|
||||
[HideInEditor]
|
||||
public class CachedCustomAnimGraphNodesCollection : CachedTypesCollection
|
||||
{
|
||||
private List<NodeArchetype> _archetypes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance has any type from game scripts (or editor scripts) - those can be reloaded at runtime so prevent crashes.
|
||||
/// </summary>
|
||||
public bool HasTypeFromGameScripts { get; protected set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public CachedCustomAnimGraphNodesCollection(int capacity, ScriptType type, Func<ScriptType, bool> checkFunc, Func<Assembly, bool> checkAssembly)
|
||||
: base(capacity, type, checkFunc, checkAssembly)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the cached archetypes.
|
||||
/// </summary>
|
||||
/// <returns>The archetypes collection.</returns>
|
||||
public List<NodeArchetype> GetArchetypes()
|
||||
{
|
||||
Get();
|
||||
return _archetypes;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Search()
|
||||
{
|
||||
base.Search();
|
||||
|
||||
HasTypeFromGameScripts = false;
|
||||
|
||||
if (_list.Count == 0)
|
||||
{
|
||||
_archetypes?.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_archetypes == null)
|
||||
_archetypes = new List<NodeArchetype>(_list.Capacity);
|
||||
_archetypes.Clear();
|
||||
|
||||
for (int i = _list.Count - 1; i >= 0 && _list.Count > 0; i--)
|
||||
{
|
||||
var nodeType = _list[i].Type;
|
||||
|
||||
var methods = nodeType.GetMethods(BindingFlags.Static | BindingFlags.Public);
|
||||
|
||||
for (int j = 0; j < methods.Length; j++)
|
||||
{
|
||||
var arch = GetArchetype(methods[j]);
|
||||
|
||||
if (arch != null)
|
||||
{
|
||||
// Cache type
|
||||
_archetypes.Add(arch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NodeArchetype GetArchetype(MethodBase method)
|
||||
{
|
||||
// Validate method signature
|
||||
if (!method.IsStatic ||
|
||||
!method.IsPublic ||
|
||||
method as MethodInfo == null ||
|
||||
((MethodInfo)method).ReturnType != typeof(NodeArchetype) ||
|
||||
method.GetParameters().Length != 0 ||
|
||||
method.IsGenericMethod)
|
||||
return null;
|
||||
|
||||
// Invoke method
|
||||
try
|
||||
{
|
||||
var arch = (NodeArchetype)method.Invoke(null, null);
|
||||
|
||||
// Validate archetype
|
||||
if (arch.Tag != null || string.IsNullOrEmpty(arch.Title))
|
||||
{
|
||||
Debug.LogWarning(string.Format("Method {0} from {1} returned invalid node archetype. Tag must be null and title must be specified.", method, method.DeclaringType));
|
||||
return null;
|
||||
}
|
||||
if (arch.DefaultValues == null || arch.DefaultValues.Length < 2 || string.IsNullOrEmpty(arch.DefaultValues[0] as string) || string.IsNullOrEmpty(arch.DefaultValues[1] as string))
|
||||
{
|
||||
Debug.LogWarning(string.Format("Method {0} from {1} returned invalid node archetype. Default values are invalid. DefaultValues[0] must specify the C# runtime controller typename. DefaultValues[1] must specify the node group name.", method, method.DeclaringType));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate node type
|
||||
var typeName = Surface.Archetypes.Custom.GetNodeTypeName(arch);
|
||||
var type = Scripting.TypeUtils.GetType(typeName).Type;
|
||||
if (type == null)
|
||||
{
|
||||
Debug.LogWarning(string.Format("Method {0} from {1} returned invalid node archetype. Failed to find node logic defined in type {2}.", method, method.DeclaringType, typeName));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if type comes from scripts that can be reloaded at runtime
|
||||
HasTypeFromGameScripts |= FlaxEngine.Scripting.IsTypeFromGameScripts(method.DeclaringType) || FlaxEngine.Scripting.IsTypeFromGameScripts(type);
|
||||
|
||||
return arch;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Failed to get the custom node archetype.");
|
||||
Debug.LogWarning(ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearTypes()
|
||||
{
|
||||
base.ClearTypes();
|
||||
|
||||
_archetypes?.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached types collection container.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
public class CachedTypesCollection
|
||||
{
|
||||
private bool _hasValidData;
|
||||
private readonly int _capacity;
|
||||
private readonly Func<ScriptType, bool> _checkFunc;
|
||||
private readonly Func<Assembly, bool> _checkAssembly;
|
||||
|
||||
/// <summary>
|
||||
/// The type.
|
||||
/// </summary>
|
||||
protected readonly ScriptType _type;
|
||||
|
||||
/// <summary>
|
||||
/// The list.
|
||||
/// </summary>
|
||||
protected List<ScriptType> _list;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CachedTypesCollection"/> class.
|
||||
/// </summary>
|
||||
/// <param name="capacity">The initial collection capacity.</param>
|
||||
/// <param name="type">The type of things to find. It can be attribute to find all classes with the given attribute defined.</param>
|
||||
/// <param name="checkFunc">Additional callback used to check if the given type is valid. Returns true if add type, otherwise false.</param>
|
||||
/// <param name="checkAssembly">Additional callback used to check if the given assembly is valid. Returns true if search for types in the given assembly, otherwise false.</param>
|
||||
public CachedTypesCollection(int capacity, ScriptType type, Func<ScriptType, bool> checkFunc, Func<Assembly, bool> checkAssembly)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_type = type;
|
||||
_checkFunc = checkFunc;
|
||||
_checkAssembly = checkAssembly;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the types from the all loaded assemblies (including project scripts and scripts from the plugins).
|
||||
/// </summary>
|
||||
/// <returns>The types collection (readonly).</returns>
|
||||
public List<ScriptType> Get()
|
||||
{
|
||||
if (!_hasValidData)
|
||||
{
|
||||
if (_list == null)
|
||||
_list = new List<ScriptType>(_capacity);
|
||||
_list.Clear();
|
||||
_hasValidData = true;
|
||||
|
||||
Editor.Log("Searching for valid " + _type);
|
||||
Profiler.BeginEvent("Search " + _type);
|
||||
var start = DateTime.Now;
|
||||
|
||||
Search();
|
||||
|
||||
var end = DateTime.Now;
|
||||
Profiler.EndEvent();
|
||||
Editor.Log(string.Format("Found {0} types (in {1} ms)", _list.Count, (int)(end - start).TotalMilliseconds));
|
||||
}
|
||||
|
||||
return _list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for the types and fills with data.
|
||||
/// </summary>
|
||||
protected virtual void Search()
|
||||
{
|
||||
// Special case for attributes
|
||||
if (_type.Type != null && new ScriptType(typeof(Attribute)).IsAssignableFrom(_type))
|
||||
{
|
||||
TypeUtils.GetTypesWithAttributeDefined(_type.Type, _list, _checkFunc, _checkAssembly);
|
||||
}
|
||||
else
|
||||
{
|
||||
TypeUtils.GetDerivedTypes(_type, _list, _checkFunc, _checkAssembly);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the types.
|
||||
/// </summary>
|
||||
public virtual void ClearTypes()
|
||||
{
|
||||
_list?.Clear();
|
||||
_hasValidData = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using Object = System.Object;
|
||||
|
||||
namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Source code editing module.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Modules.EditorModule" />
|
||||
public sealed class CodeEditingModule : EditorModule
|
||||
{
|
||||
private sealed class CachedVisualScriptPropertyTypesCollection : CachedTypesCollection
|
||||
{
|
||||
public CachedVisualScriptPropertyTypesCollection(int capacity, Func<Assembly, bool> checkAssembly)
|
||||
: base(capacity, new ScriptType(typeof(Object)), CheckFunc, checkAssembly)
|
||||
{
|
||||
}
|
||||
|
||||
private static bool CheckFunc(ScriptType type)
|
||||
{
|
||||
if (!type.IsPublic)
|
||||
return false;
|
||||
var objectType = new ScriptType(typeof(FlaxEngine.Object));
|
||||
return type.IsEnum || type == objectType || objectType.IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Search()
|
||||
{
|
||||
// Add in-build types
|
||||
_list.Add(new ScriptType(typeof(bool)));
|
||||
_list.Add(new ScriptType(typeof(byte)));
|
||||
_list.Add(new ScriptType(typeof(short)));
|
||||
_list.Add(new ScriptType(typeof(ushort)));
|
||||
_list.Add(new ScriptType(typeof(int)));
|
||||
_list.Add(new ScriptType(typeof(uint)));
|
||||
_list.Add(new ScriptType(typeof(long)));
|
||||
_list.Add(new ScriptType(typeof(ulong)));
|
||||
_list.Add(new ScriptType(typeof(float)));
|
||||
_list.Add(new ScriptType(typeof(double)));
|
||||
_list.Add(new ScriptType(typeof(string)));
|
||||
_list.Add(new ScriptType(typeof(Vector2)));
|
||||
_list.Add(new ScriptType(typeof(Vector3)));
|
||||
_list.Add(new ScriptType(typeof(Vector4)));
|
||||
_list.Add(new ScriptType(typeof(Color)));
|
||||
_list.Add(new ScriptType(typeof(Quaternion)));
|
||||
_list.Add(new ScriptType(typeof(BoundingBox)));
|
||||
_list.Add(new ScriptType(typeof(BoundingSphere)));
|
||||
_list.Add(new ScriptType(typeof(Rectangle)));
|
||||
_list.Add(new ScriptType(typeof(Matrix)));
|
||||
_list.Add(new ScriptType(typeof(Ray)));
|
||||
_list.Add(new ScriptType(typeof(Guid)));
|
||||
|
||||
base.Search();
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CachedAllTypesCollection : CachedTypesCollection
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public CachedAllTypesCollection(int capacity, ScriptType type, Func<ScriptType, bool> checkFunc, Func<Assembly, bool> checkAssembly)
|
||||
: base(capacity, type, checkFunc, checkAssembly)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Search()
|
||||
{
|
||||
// Add in-build types
|
||||
_list.Add(new ScriptType(typeof(void)));
|
||||
_list.Add(new ScriptType(typeof(bool)));
|
||||
_list.Add(new ScriptType(typeof(byte)));
|
||||
_list.Add(new ScriptType(typeof(short)));
|
||||
_list.Add(new ScriptType(typeof(ushort)));
|
||||
_list.Add(new ScriptType(typeof(int)));
|
||||
_list.Add(new ScriptType(typeof(uint)));
|
||||
_list.Add(new ScriptType(typeof(long)));
|
||||
_list.Add(new ScriptType(typeof(ulong)));
|
||||
_list.Add(new ScriptType(typeof(float)));
|
||||
_list.Add(new ScriptType(typeof(double)));
|
||||
_list.Add(new ScriptType(typeof(string)));
|
||||
_list.Add(new ScriptType(typeof(Guid)));
|
||||
_list.Add(new ScriptType(typeof(DateTime)));
|
||||
_list.Add(new ScriptType(typeof(TimeSpan)));
|
||||
|
||||
base.Search();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<ISourceCodeEditor> _editors = new List<ISourceCodeEditor>(8);
|
||||
private ISourceCodeEditor _selectedEditor;
|
||||
private bool _autoGenerateScriptsProjectFiles;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source code editors registered for usage in editor.
|
||||
/// </summary>
|
||||
public IReadOnlyList<ISourceCodeEditor> Editors => _editors;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when source code editor gets added.
|
||||
/// </summary>
|
||||
public event Action<ISourceCodeEditor> EditorAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when source code editor gets removed.
|
||||
/// </summary>
|
||||
public event Action<ISourceCodeEditor> EditorRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when selected source code editor gets changed.
|
||||
/// </summary>
|
||||
public event Action<ISourceCodeEditor> SelectedEditorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the selected editor.
|
||||
/// </summary>
|
||||
public ISourceCodeEditor SelectedEditor
|
||||
{
|
||||
get => _selectedEditor;
|
||||
set
|
||||
{
|
||||
if (_selectedEditor != value)
|
||||
{
|
||||
if (value != null && !_editors.Contains(value))
|
||||
throw new ArgumentException("Cannot selected editor that has not been added to the editors list.");
|
||||
|
||||
var prev = _selectedEditor;
|
||||
_selectedEditor = value;
|
||||
|
||||
Editor.Log("Select code editor " + (_selectedEditor?.Name ?? "null"));
|
||||
|
||||
prev?.OnDeselected(Editor);
|
||||
_selectedEditor?.OnSelected(Editor);
|
||||
SelectedEditorChanged?.Invoke(_selectedEditor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when cached scripting types lists are cleared (eg. on global invalidation or scripting reload).
|
||||
/// </summary>
|
||||
public event Action TypesCleared;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when types information gets modified (eg. after scripting reload or when script asset gets saved). Can be used to refresh any cached locally script types.
|
||||
/// </summary>
|
||||
public event Action TypesChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The all types collection from all assemblies (excluding C# system libraries). Includes only primitive and basic types from std lib.
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection All = new CachedAllTypesCollection(8096, new ScriptType(typeof(object)), type => true, HasAssemblyValidAnyTypes);
|
||||
|
||||
/// <summary>
|
||||
/// The all valid types collection for the Visual Script property types (includes basic types like int/float, structures, object references).
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection VisualScriptPropertyTypes = new CachedVisualScriptPropertyTypesCollection(8096, HasAssemblyValidAnyTypes);
|
||||
|
||||
/// <summary>
|
||||
/// The actors collection.
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection Actors = new CachedTypesCollection(1024, new ScriptType(typeof(Actor)), IsTypeValidScriptingType, HasAssemblyValidScriptingTypes);
|
||||
|
||||
/// <summary>
|
||||
/// The scripts collection.
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection Scripts = new CachedTypesCollection(1024, new ScriptType(typeof(Script)), IsTypeValidScriptingType, HasAssemblyValidScriptingTypes);
|
||||
|
||||
/// <summary>
|
||||
/// The control types collection (for game UI).
|
||||
/// </summary>
|
||||
public readonly CachedTypesCollection Controls = new CachedTypesCollection(64, new ScriptType(typeof(Control)), IsTypeValidScriptingTypeControls, HasAssemblyValidScriptingTypes);
|
||||
|
||||
/// <summary>
|
||||
/// The Animation Graph custom nodes collection.
|
||||
/// </summary>
|
||||
public readonly CachedCustomAnimGraphNodesCollection AnimGraphNodes = new CachedCustomAnimGraphNodesCollection(32, new ScriptType(typeof(AnimationGraph.CustomNodeArchetypeFactoryAttribute)), IsTypeValidScriptingType, HasAssemblyValidScriptingTypes);
|
||||
|
||||
internal CodeEditingModule(Editor editor)
|
||||
: base(editor)
|
||||
{
|
||||
}
|
||||
|
||||
internal InBuildSourceCodeEditor GetInBuildEditor(CodeEditorTypes editorType)
|
||||
{
|
||||
for (var i = 0; i < Editors.Count; i++)
|
||||
{
|
||||
if (Editors[i] is InBuildSourceCodeEditor inBuild && inBuild.Type == editorType)
|
||||
return inBuild;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the editor to the collection.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
public void AddEditor(ISourceCodeEditor editor)
|
||||
{
|
||||
if (editor == null)
|
||||
throw new ArgumentNullException();
|
||||
if (_editors.Contains(editor))
|
||||
throw new ArgumentException("Editor already added.");
|
||||
|
||||
Editor.Log("Add code editor " + editor.Name);
|
||||
|
||||
_editors.Add(editor);
|
||||
editor.OnAdded(Editor);
|
||||
EditorAdded?.Invoke(editor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the editor from the collection.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
public void RemoveEditor(ISourceCodeEditor editor)
|
||||
{
|
||||
if (editor == null)
|
||||
throw new ArgumentNullException();
|
||||
if (!_editors.Contains(editor))
|
||||
throw new ArgumentException("Editor not added.");
|
||||
|
||||
if (_selectedEditor == editor)
|
||||
SelectedEditor = null;
|
||||
|
||||
Editor.Log("Remove code editor " + editor.Name);
|
||||
|
||||
_editors.Remove(editor);
|
||||
editor.OnRemoved(Editor);
|
||||
EditorRemoved?.Invoke(editor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the solution file using the selected selected code editor.
|
||||
/// </summary>
|
||||
public void OpenSolution()
|
||||
{
|
||||
if (_selectedEditor == null)
|
||||
return;
|
||||
|
||||
_selectedEditor.OpenSolution();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the file using the selected code editor.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to open.</param>
|
||||
/// <param name="line">The line number to navigate to. Use 0 to not use it.</param>
|
||||
public void OpenFile(string path, int line = 0)
|
||||
{
|
||||
if (_selectedEditor == null)
|
||||
return;
|
||||
|
||||
_selectedEditor.OpenFile(path, line);
|
||||
}
|
||||
|
||||
private unsafe void GetInBuildEditors()
|
||||
{
|
||||
var resultArray = stackalloc int[(int)CodeEditorTypes.MAX];
|
||||
ScriptsBuilder.GetExistingEditors(resultArray, (int)CodeEditorTypes.MAX);
|
||||
for (int i = 0; i < (int)CodeEditorTypes.MAX; i++)
|
||||
{
|
||||
if (resultArray[i] != 0)
|
||||
{
|
||||
var editor = new InBuildSourceCodeEditor((CodeEditorTypes)i);
|
||||
AddEditor(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnInit()
|
||||
{
|
||||
ScriptsBuilder.ScriptsReload += OnScriptsReload;
|
||||
ScriptsBuilder.ScriptsReloadEnd += OnScriptsReloadEnd;
|
||||
Editor.Options.OptionsChanged += OnOptionsChanged;
|
||||
|
||||
// Add default code editors (in-build)
|
||||
GetInBuildEditors();
|
||||
AddEditor(new DefaultSourceCodeEditor());
|
||||
|
||||
// Editor options are loaded before this module so pick the code editor
|
||||
OnOptionsChanged(Editor.Options.Options);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndInit()
|
||||
{
|
||||
// Special case when failed to load scripts on editor start - clear types later so all types will be cached if needed
|
||||
if (!FlaxEngine.Scripting.HasGameModulesLoaded())
|
||||
{
|
||||
ScriptsBuilder.CompilationSuccess += OnFirstCompilationSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFirstCompilationSuccess()
|
||||
{
|
||||
ScriptsBuilder.CompilationSuccess -= OnFirstCompilationSuccess;
|
||||
|
||||
ClearTypes();
|
||||
}
|
||||
|
||||
private void OnOptionsChanged(EditorOptions options)
|
||||
{
|
||||
// Sync options
|
||||
_autoGenerateScriptsProjectFiles = options.SourceCode.AutoGenerateScriptsProjectFiles;
|
||||
|
||||
// Sync code editor
|
||||
ISourceCodeEditor editor = null;
|
||||
var codeEditor = options.SourceCode.SourceCodeEditor;
|
||||
if (codeEditor != "None")
|
||||
{
|
||||
foreach (var e in Editor.Instance.CodeEditing.Editors)
|
||||
{
|
||||
if (string.Equals(codeEditor, e.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
editor = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Editor.Instance.CodeEditing.SelectedEditor = editor;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnUpdate()
|
||||
{
|
||||
base.OnUpdate();
|
||||
|
||||
// Automatic project files generation after workspace modifications
|
||||
if (_autoGenerateScriptsProjectFiles && ScriptsBuilder.IsSourceWorkspaceDirty)
|
||||
{
|
||||
Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnExit()
|
||||
{
|
||||
// Cleanup
|
||||
SelectedEditor = null;
|
||||
_editors.Clear();
|
||||
|
||||
ScriptsBuilder.ScriptsReload -= OnScriptsReload;
|
||||
ScriptsBuilder.ScriptsReloadEnd -= OnScriptsReloadEnd;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all the cached types.
|
||||
/// </summary>
|
||||
public void ClearTypes()
|
||||
{
|
||||
// Invalidate cached types
|
||||
All.ClearTypes();
|
||||
VisualScriptPropertyTypes.ClearTypes();
|
||||
Actors.ClearTypes();
|
||||
Scripts.ClearTypes();
|
||||
Controls.ClearTypes();
|
||||
AnimGraphNodes.ClearTypes();
|
||||
TypesCleared?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the types change event to inform the Editor.
|
||||
/// </summary>
|
||||
public void OnTypesChanged()
|
||||
{
|
||||
TypesChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void OnScriptsReload()
|
||||
{
|
||||
ClearTypes();
|
||||
}
|
||||
|
||||
private void OnScriptsReloadEnd()
|
||||
{
|
||||
OnTypesChanged();
|
||||
}
|
||||
|
||||
private static bool IsTypeValidScriptingType(ScriptType t)
|
||||
{
|
||||
return !t.IsGenericType && !t.IsAbstract && !t.HasAttribute(typeof(HideInEditorAttribute), false);
|
||||
}
|
||||
|
||||
private static bool IsTypeValidScriptingTypeControls(ScriptType t)
|
||||
{
|
||||
// Skip Flax Editor internal controls from usage in game
|
||||
if (t.Type?.Namespace?.StartsWith("FlaxEditor") ?? false)
|
||||
return false;
|
||||
|
||||
return IsTypeValidScriptingType(t);
|
||||
}
|
||||
|
||||
private static bool HasAssemblyValidAnyTypes(Assembly assembly)
|
||||
{
|
||||
// Skip assemblies from in-build Mono directory
|
||||
var codeBase = assembly.CodeBase;
|
||||
return string.IsNullOrEmpty(codeBase) || !codeBase.Contains("/Mono/lib/mono/");
|
||||
}
|
||||
|
||||
private static bool HasAssemblyValidScriptingTypes(Assembly a)
|
||||
{
|
||||
// Check engine assembly
|
||||
if (a.GetName().Name == Utilities.Utils.FlaxEngineAssemblyName)
|
||||
return true;
|
||||
|
||||
// Skip assemblies not referencing engine assembly
|
||||
var references = a.GetReferencedAssemblies();
|
||||
return references.Any(x => x.Name == Utilities.Utils.FlaxEngineAssemblyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Default source code editor. Picks the best available editor on the current the platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Modules.SourceCodeEditing.ISourceCodeEditor" />
|
||||
internal class DefaultSourceCodeEditor : ISourceCodeEditor
|
||||
{
|
||||
private ISourceCodeEditor _currentEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DefaultSourceCodeEditor"/> class.
|
||||
/// </summary>
|
||||
public DefaultSourceCodeEditor()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnEditorAdded(ISourceCodeEditor editor)
|
||||
{
|
||||
if (editor == this)
|
||||
return;
|
||||
|
||||
UpdateCurrentEditor();
|
||||
}
|
||||
|
||||
private void OnEditorRemoved(ISourceCodeEditor editor)
|
||||
{
|
||||
if (editor != _currentEditor)
|
||||
return;
|
||||
|
||||
UpdateCurrentEditor();
|
||||
}
|
||||
|
||||
private void UpdateCurrentEditor()
|
||||
{
|
||||
var codeEditing = Editor.Instance.CodeEditing;
|
||||
|
||||
// Favor the newest Visual Studio
|
||||
for (int i = (int)CodeEditorTypes.VS2019; i >= (int)CodeEditorTypes.VS2008; i--)
|
||||
{
|
||||
var visualStudio = codeEditing.GetInBuildEditor((CodeEditorTypes)i);
|
||||
if (visualStudio != null)
|
||||
{
|
||||
_currentEditor = visualStudio;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback default editor (always valid)
|
||||
_currentEditor = codeEditing.GetInBuildEditor(CodeEditorTypes.SystemDefault);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => "Default";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GenerateProjectCustomArgs => null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenSolution()
|
||||
{
|
||||
_currentEditor?.OpenSolution();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenFile(string path, int line)
|
||||
{
|
||||
_currentEditor?.OpenFile(path, line);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnFileAdded(string path)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnSelected(Editor editor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnDeselected(Editor editor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnAdded(Editor editor)
|
||||
{
|
||||
editor.CodeEditing.EditorAdded += OnEditorAdded;
|
||||
editor.CodeEditing.EditorRemoved += OnEditorRemoved;
|
||||
|
||||
UpdateCurrentEditor();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnRemoved(Editor editor)
|
||||
{
|
||||
_currentEditor = null;
|
||||
|
||||
editor.CodeEditing.EditorAdded -= OnEditorAdded;
|
||||
editor.CodeEditing.EditorRemoved -= OnEditorRemoved;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for source code editing plugins.
|
||||
/// </summary>
|
||||
public interface ISourceCodeEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the editor name. Used to show in the UI.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the custom arguments for the Flax.Build tool to add when generating project files for this code editor. Can be used to pick a different project files generator. Empty by default.
|
||||
/// </summary>
|
||||
string GenerateProjectCustomArgs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Opens the solution file (source code project file).
|
||||
/// </summary>
|
||||
void OpenSolution();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the source file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file path to open.</param>
|
||||
/// <param name="line">The line number to navigate to. Use 0 to not use it.</param>
|
||||
void OpenFile(string path, int line);
|
||||
|
||||
/// <summary>
|
||||
/// Called when source file gets added to the workspace. Can be used to automatically include new files into the project files.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
void OnFileAdded(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Called when editor gets selected.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
void OnSelected(Editor editor);
|
||||
|
||||
/// <summary>
|
||||
/// Called when editor gets deselected.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
void OnDeselected(Editor editor);
|
||||
|
||||
/// <summary>
|
||||
/// Called when editor gets added.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
void OnAdded(Editor editor);
|
||||
|
||||
/// <summary>
|
||||
/// Called when editor gets removed.
|
||||
/// </summary>
|
||||
/// <param name="editor">The editor.</param>
|
||||
void OnRemoved(Editor editor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
|
||||
namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// In-build source code editor.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Modules.SourceCodeEditing.ISourceCodeEditor" />
|
||||
internal class InBuildSourceCodeEditor : ISourceCodeEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the editor.
|
||||
/// </summary>
|
||||
public readonly CodeEditorTypes Type;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InBuildSourceCodeEditor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The type.</param>
|
||||
public InBuildSourceCodeEditor(CodeEditorTypes type)
|
||||
{
|
||||
Type = type;
|
||||
switch (type)
|
||||
{
|
||||
case CodeEditorTypes.Custom:
|
||||
Name = "Custom";
|
||||
break;
|
||||
case CodeEditorTypes.SystemDefault:
|
||||
Name = "System Default";
|
||||
break;
|
||||
case CodeEditorTypes.VS2008:
|
||||
Name = "Visual Studio 2008";
|
||||
break;
|
||||
case CodeEditorTypes.VS2010:
|
||||
Name = "Visual Studio 2010";
|
||||
break;
|
||||
case CodeEditorTypes.VS2012:
|
||||
Name = "Visual Studio 2012";
|
||||
break;
|
||||
case CodeEditorTypes.VS2013:
|
||||
Name = "Visual Studio 2013";
|
||||
break;
|
||||
case CodeEditorTypes.VS2015:
|
||||
Name = "Visual Studio 2015";
|
||||
break;
|
||||
case CodeEditorTypes.VS2017:
|
||||
Name = "Visual Studio 2017";
|
||||
break;
|
||||
case CodeEditorTypes.VS2019:
|
||||
Name = "Visual Studio 2019";
|
||||
break;
|
||||
case CodeEditorTypes.VSCode:
|
||||
Name = "Visual Studio Code";
|
||||
break;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GenerateProjectCustomArgs
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case CodeEditorTypes.VSCode: return "-vscode";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenSolution()
|
||||
{
|
||||
CodeEditingManager.OpenSolution(Type);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OpenFile(string path, int line)
|
||||
{
|
||||
CodeEditingManager.OpenFile(Type, path, line);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnFileAdded(string path)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case CodeEditorTypes.VS2008:
|
||||
case CodeEditorTypes.VS2010:
|
||||
case CodeEditorTypes.VS2012:
|
||||
case CodeEditorTypes.VS2013:
|
||||
case CodeEditorTypes.VS2015:
|
||||
case CodeEditorTypes.VS2017:
|
||||
case CodeEditorTypes.VS2019:
|
||||
// TODO: finish dynamic files adding to the project
|
||||
//Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
|
||||
break;
|
||||
default:
|
||||
CodeEditingManager.OnFileAdded(Type, path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnSelected(Editor editor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnDeselected(Editor editor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnAdded(Editor editor)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnRemoved(Editor editor)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user