private static void AddUndocumentApis(UnityApi unityApi, Version apiVersion) { // From AssetPostprocessingInternal var type = unityApi.FindType("AssetPostprocessor"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnPreprocessAssembly", false, false, ApiType.Void, apiVersion, undocumented: true); eventFunction.AddParameter("pathName", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnGeneratedCSProjectFiles", true, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Technically, return type is optional eventFunction = new UnityApiEventFunction("OnPreGeneratingCSProjectFiles", true, false, ApiType.Bool, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } // From AssetModificationProcessorInternal type = unityApi.FindType("AssetModificationProcessor"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnStatusUpdated", true, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } // ScriptableObject // From Shawn White @ Unity (https://github.com/JetBrains/resharper-unity/issues/79#issuecomment-266727851): // OnValidate's behavior on ScriptableObject is the same as on MonoBehaviour. OnValidate is a non-static // method which is invoked from native and isn't picky about visibility (it'll get invoked regardless of // visibility). OnValidate is different from the other magic methods in that it only gets invoked from // the Editor. A good practice is to wrap OnValidate with #if UNITY_EDITOR so that the function can be // stripped out for deployment. // // To expand on ScriptableObject a bit. Native Unity code doesn't distinguish between MonoBehaviour and // ScriptableObject. They are the same native type (it's confusing, I know, and everyone gets tripped up // by it that looks at the native code base). The only difference is in how the object is used, // MonoBehaviours live on GameObjects, while ScriptableObjects can live alone.So in general, all magic // methods that would make sense without a GameObject context should work and be implemented for // ScriptableObjects. Off the top of my head this includes, Awake, OnEnable, OnDisable, OnDestroy, // OnValidate, and Reset, but there could be more. type = unityApi.FindType("ScriptableObject"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnValidate", false, false, ApiType.Void, apiVersion, description: "This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("Reset", false, false, ApiType.Void, apiVersion, description: "Reset to default values.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } }
private UnityApiEventFunction ParseMessage(ApiNode message, Version apiVersion, string hintNamespace) { var link = message.SelectOne(@"td.lbl/a"); var desc = message.SelectOne(@"td.desc"); if (link == null || desc == null) { return(null); } var detailsPath = link[@"href"]; if (string.IsNullOrWhiteSpace(detailsPath)) { return(null); } var path = Path.Combine(myScriptReferenceRelativePath, detailsPath); if (!File.Exists(path)) { return(null); } var detailsDoc = ApiNode.Load(path); var details = detailsDoc?.SelectOne(@"//div.content/div.section"); var signature = details?.SelectOne(@"div.mb20.clear/h1.heading.inherit"); var staticNode = details?.SelectOne(@"div.subsection/p/code.varname[text()='static']"); if (signature == null) { return(null); } var isCoroutine = CoroutineRegex.IsMatch(details.Text); var messageName = link.Text; var returnType = ApiType.Void; string[] argumentNames = null; var isStaticFromExample = false; var example = PickExample(details); if (example != null) { var tuple = ParseDetailsFromExample(messageName, example, hintNamespace); returnType = tuple.Item1; argumentNames = tuple.Item2; isStaticFromExample = tuple.Item3; } var docPath = Path.Combine(myScriptReferenceRelativePath, detailsPath); var eventFunction = new UnityApiEventFunction(messageName, staticNode != null || isStaticFromExample, isCoroutine, returnType, apiVersion, desc.Text, docPath); ParseParameters(eventFunction, signature, details, hintNamespace, argumentNames); return(eventFunction); }
public void MergeEventFunction(UnityApiEventFunction newFunction, Version apiVersion) { UpdateSupportedVersion(apiVersion); var newFunctionSig = newFunction.ToString(); foreach (var eventFunction in myEventFunctions) { if (eventFunction.ToString() == newFunctionSig) { // Prefer an existing documented function that covers this version, than an undocumented one. This // means we will automatically replace our undocumented functions with documented versions without // having to explicitly add an end version if (newFunction.IsUndocumented && !eventFunction.IsUndocumented && eventFunction.MinimumVersion <= apiVersion && eventFunction.MaximumVersion >= apiVersion) { return; } // If the documented state of both functions is the same, update the function if (newFunction.IsUndocumented == eventFunction.IsUndocumented) { eventFunction.Update(newFunction, apiVersion); return; } } } // Not a match. We either haven't found this function before, // or a parameter or return type is different myEventFunctions.Add(newFunction); }
public static UnityApiType ImportFrom(XElement element, HasVersionRange apiVersions) { var ns = element.Attribute("ns").Value; var name = element.Attribute("name").Value; var kind = element.Attribute("kind").Value; var path = element.Attribute("path").Value; var type = new UnityApiType(ns, name, kind, path, new Version(0, 0)); type.ImportVersionRange(element, apiVersions); foreach (var message in element.Descendants("message")) { type.myEventFunctions.Add(UnityApiEventFunction.ImportFrom(message, type)); } return(type); }
public void Update(UnityApiEventFunction function, Version apiVersion) { if (UpdateSupportedVersion(apiVersion)) { if (function.myDescription != myDescription && !string.IsNullOrEmpty(function.myDescription)) { myDescription = function.myDescription; } for (var i = 0; i < myParameters.Count; i++) { myParameters[i].Update(function.myParameters[i]); } } }
private static void AddUndocumentApis(UnityApi unityApi, Version apiVersion) { // From AssetPostprocessingInternal var type = unityApi.FindType("AssetPostprocessor"); var eventFunction = new UnityApiEventFunction("OnPreprocessAssembly", false, ApiType.Void, apiVersion, undocumented: true); eventFunction.AddParameter("pathName", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnGeneratedCSProjectFiles", true, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Technically, return type is optional eventFunction = new UnityApiEventFunction("OnPreGeneratingCSProjectFiles", true, ApiType.Bool, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // From AssetModificationProcessorInternal type = unityApi.FindType("AssetModificationProcessor"); eventFunction = new UnityApiEventFunction("OnStatusUpdated", true, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // ScriptableObject // From Shawn White @ Unity (https://github.com/JetBrains/resharper-unity/issues/79#issuecomment-266727851): // OnValidate's behavior on ScriptableObject is the same as on MonoBehaviour. OnValidate is a non-static // method which is invoked from native and isn't picky about visibility (it'll get invoked regardless of // visibility). OnValidate is different from the other magic methods in that it only gets invoked from // the Editor. A good practice is to wrap OnValidate with #if UNITY_EDITOR so that the function can be // stripped out for deployment. // // To expand on ScriptableObject a bit. Native Unity code doesn't distinguish between MonoBehaviour and // ScriptableObject. They are the same native type (it's confusing, I know, and everyone gets tripped up // by it that looks at the native code base). The only difference is in how the object is used, // MonoBehaviours live on GameObjects, while ScriptableObjects can live alone.So in general, all magic // methods that would make sense without a GameObject context should work and be implemented for // ScriptableObjects. Off the top of my head this includes, Awake, OnEnable, OnDisable, OnDestroy, // OnValidate, and Reset, but there could be more. type = unityApi.FindType("ScriptableObject"); eventFunction = new UnityApiEventFunction("OnValidate", false, ApiType.Void, apiVersion, description: "This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("Reset", false, ApiType.Void, apiVersion, description: "Reset to default values.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); }
public void MergeEventFunction(UnityApiEventFunction newFunction, Version apiVersion) { UpdateSupportedVersion(apiVersion); var newFunctionSig = newFunction.ToString(); foreach (var eventFunction in myEventFunctions) { // If the signature matches, we've already got it, just // make sure it's up to date (newer docs take precedence // for e.g. param names, description, etc) if (eventFunction.ToString() == newFunctionSig) { eventFunction.Update(newFunction, apiVersion); return; } } // Not a match. We either haven't found this function before, // or a parameter or return type is different myEventFunctions.Add(newFunction); }
public static UnityApiEventFunction ImportFrom(XElement message, HasVersionRange versions) { var name = message.Attribute("name").Value; var isStatic = bool.Parse(message.Attribute("static").Value); var coroutineAttribute = message.Attribute("coroutine"); var isCoroutine = coroutineAttribute != null && bool.Parse(coroutineAttribute.Value); var description = message.Attribute("description")?.Value; var path = message.Attribute("path")?.Value; var undocumentedAttribute = message.Attribute("undocumented"); var isUndocumented = undocumentedAttribute != null && bool.Parse(undocumentedAttribute.Value); var returns = message.Descendants("returns").First(); var type = returns.Attribute("type").Value; var isArray = bool.Parse(returns.Attribute("array").Value); var returnType = new ApiType(type + (isArray ? "[]" : string.Empty)); var function = new UnityApiEventFunction(name, isStatic, isCoroutine, returnType, new Version(int.MaxValue, 0), description, path, isUndocumented); function.ImportVersionRange(message, versions); foreach (var parameter in message.Descendants("parameter")) { function.myParameters.Add(UnityApiParameter.ImportFrom(parameter)); } return(function); }
private static void ParseParameters(UnityApiEventFunction eventFunction, ApiNode signature, ApiNode details, string owningMessageNamespace, string[] argumentNames) { // Capture the arguments string. Note that this might be `string s, int i` or `string, int` var match = CaptureArgumentsRegex.Match(signature.Text); if (!match.Success) { return; } var argumentString = match.Groups["args"].Value; var argumentStrings = argumentString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(s => s.Trim()) .ToArray(); var total = argumentStrings.Length; var arguments = argumentStrings.Select((s, i) => new Argument(s, i, total, owningMessageNamespace)).ToArray(); ResolveArguments(details, arguments, argumentNames); foreach (var argument in arguments) { eventFunction.AddParameter(argument.Name, argument.Type, argument.Description); } }
private static void ParseParameters(UnityApiEventFunction eventFunction, ApiNode signature, ApiNode details, string owningMessageNamespace, string[] argumentNames) { // E.g. OnCollisionExit2D(Collision2D) - doesn't always include the argument name // Hopefully, we parsed the argument name from the example var argumentString = SigRegex.Replace(signature.Text, "$2$3"); if (string.IsNullOrWhiteSpace(argumentString)) { return; } var argumentStrings = argumentString.Split(',') .Select(s => s.Trim()) .ToArray(); var total = argumentStrings.Length; var arguments = argumentStrings.Select((s, i) => new Argument(s, i, total, owningMessageNamespace)).ToArray(); ResolveArguments(details, arguments, argumentNames); foreach (var argument in arguments) { eventFunction.AddParameter(argument.Name, argument.Type, argument.Description); } }
private UnityApiEventFunction ParseMessage(string className, ApiNode message, Version apiVersion, string hintNamespace, HashSet <string> processed) { var link = message.SelectOne(@"td.lbl/a"); var desc = message.SelectOne(@"td.desc"); if (link == null || desc == null) { return(null); } var detailsPath = link[@"href"]; if (string.IsNullOrWhiteSpace(detailsPath)) { return(null); } var scriptReferenceRelativePath = Directory.Exists(ScriptReferenceRelativePath1) ? ScriptReferenceRelativePath1 : ScriptReferenceRelativePath2; var path = Path.Combine(scriptReferenceRelativePath, detailsPath); processed.Add(path); if (!File.Exists(path)) { return(null); } var detailsDoc = ApiNode.Load(path); var details = detailsDoc?.SelectOne(@"//div.content/div.section"); var signature = details?.SelectOne(@"div.mb20.clear/h1.heading.inherit"); var staticNode = details?.SelectOne(@"div.subsection/p/code.varname[text()='static']"); if (signature == null) { return(null); } var isCoroutine = IsCoroutineRegex.IsMatch(details.Text); var messageName = link.Text; var returnType = ApiType.Void; var argumentNames = EmptyArray <string> .Instance; var isStaticFromExample = false; var examples = PickExample(details); if (examples.Length > 0) { var tuple = ParseDetailsFromExample(messageName, examples, hintNamespace); // As of 2017.4, the docs for MonoBehaviour.OnCollisionEnter2D don't include a valid example. It demonstrates // OnTriggerEnter2D instead. Similar problems for these other methods if (tuple == null) { var fullName = $"{className}.{messageName}"; switch (fullName) { case "MonoBehaviour.OnCollisionEnter2D": case "MonoBehaviour.OnCollisionExit2D": case "MonoBehaviour.OnCollisionStay2D": case "MonoBehaviour.Start": case "MonoBehaviour.OnDestroy": Console.WriteLine( $"WARNING: Unable to parse example for {fullName}. Example incorrect in docs"); break; // case "Network.OnDisconnectedFromServer": // Bug in 2018.2 documentation // Console.WriteLine($"WARNING: Missing example for {fullName}"); // break; default: foreach (var example in examples) { Console.WriteLine(example.Text); Console.WriteLine(); } throw new InvalidOperationException($"Failed to parse example for {className}.{messageName}"); } } if (tuple != null) { returnType = tuple.Item1; argumentNames = tuple.Item2; isStaticFromExample = tuple.Item3; } } if (Equals(returnType, ApiType.IEnumerator)) { returnType = ApiType.Void; isCoroutine = true; } var docPath = Path.Combine(scriptReferenceRelativePath, detailsPath); var eventFunction = new UnityApiEventFunction(messageName, staticNode != null || isStaticFromExample, isCoroutine, returnType, apiVersion, desc.Text, docPath); ParseParameters(eventFunction, signature, details, hintNamespace, argumentNames); return(eventFunction); }
private UnityApiEventFunction ParseMessage(ApiNode message, Version apiVersion, string hintNamespace) { var link = message.SelectOne(@"td.lbl/a"); var desc = message.SelectOne(@"td.desc"); if (link == null || desc == null) return null; var detailsPath = link[@"href"]; if (string.IsNullOrWhiteSpace(detailsPath)) return null; var path = Path.Combine(myScriptReferenceRelativePath, detailsPath); if (!File.Exists(path)) return null; var detailsDoc = ApiNode.Load(path); var details = detailsDoc?.SelectOne(@"//div.content/div.section"); var signature = details?.SelectOne(@"div.mb20.clear/h1.heading.inherit"); var staticNode = details?.SelectOne(@"div.subsection/p/code.varname[text()='static']"); if (signature == null) return null; var messageName = link.Text; var returnType = ApiType.Void; string[] argumentNames = null; var example = PickExample(details); if (example != null) { var tuple = ParseDetailsFromExample(messageName, example, hintNamespace); returnType = tuple.Item1; argumentNames = tuple.Item2; } var docPath = Path.Combine(myScriptReferenceRelativePath, detailsPath); var eventFunction = new UnityApiEventFunction(messageName, staticNode != null, returnType, apiVersion, desc.Text, docPath, false); ParseParameters(eventFunction, signature, details, hintNamespace, argumentNames); return eventFunction; }
private static void AddUndocumentedApis(UnityApi unityApi, Version apiVersion) { // From AssetPostprocessingInternal var type = unityApi.FindType("AssetPostprocessor"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnPreprocessAssembly", false, false, ApiType.Void, apiVersion, undocumented: true); eventFunction.AddParameter("pathName", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); // From GitHub. Love the optimism in this one :) // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L96 var description = "This is undocumented, and a 'safeguard' for when Visual Studio gets a new release that " + "is incompatible with Unity, so that users can postprocess our csproj files to fix the issue (or " + "just completely replace them). Hopefully we'll never need this."; eventFunction = new UnityApiEventFunction("OnGeneratedCSProjectFiles", true, false, ApiType.Void, apiVersion, description, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Technically, return type is optional // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L138 description = "This callback is used by UnityVS to take over project generation from Unity"; eventFunction = new UnityApiEventFunction("OnPreGeneratingCSProjectFiles", true, false, ApiType.Bool, apiVersion, description, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // These two were added in 2018.1, as verified on GitHub // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.4/Editor/Mono/AssetPostprocessor.cs // https://github.com/Unity-Technologies/UnityCsReference/blob/2018.1/Editor/Mono/AssetPostprocessor.cs#L76 if (apiVersion >= new Version(2018, 1)) { // Technically, return type is optional // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L123 description = "This callback is used by C# code editors to modify the .csproj files."; eventFunction = new UnityApiEventFunction("OnGeneratedCSProject", true, false, ApiType.String, apiVersion, description, undocumented: true); eventFunction.AddParameter("path", ApiType.String); eventFunction.AddParameter("content", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); // Technically, return type is optional // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L108 description = "This callback is used by C# code editors to modify the .sln file"; eventFunction = new UnityApiEventFunction("OnGeneratedSlnSolution", true, false, ApiType.String, apiVersion, description, undocumented: true); eventFunction.AddParameter("path", ApiType.String); eventFunction.AddParameter("content", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); } } // From AssetModificationProcessorInternal type = unityApi.FindType("AssetModificationProcessor"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnStatusUpdated", true, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } type = unityApi.FindType("MonoBehaviour"); if (type != null) { const string description = "This callback is called if an associated RectTransform has its dimensions changed."; var eventFunction = new UnityApiEventFunction("OnRectTransformDimensionsChange", false, false, ApiType.Void, apiVersion, description, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnBeforeTransformParentChanged", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnDidApplyAnimationProperties", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnCanvasGroupChanged", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnCanvasHierarchyChanged", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } // ScriptableObject // From Shawn White @ Unity (https://github.com/JetBrains/resharper-unity/issues/79#issuecomment-266727851): // OnValidate's behavior on ScriptableObject is the same as on MonoBehaviour. OnValidate is a non-static // method which is invoked from native and isn't picky about visibility (it'll get invoked regardless of // visibility). OnValidate is different from the other magic methods in that it only gets invoked from // the Editor. A good practice is to wrap OnValidate with #if UNITY_EDITOR so that the function can be // stripped out for deployment. // // To expand on ScriptableObject a bit. Native Unity code doesn't distinguish between MonoBehaviour and // ScriptableObject. They are the same native type (it's confusing, I know, and everyone gets tripped up // by it that looks at the native code base). The only difference is in how the object is used, // MonoBehaviours live on GameObjects, while ScriptableObjects can live alone.So in general, all magic // methods that would make sense without a GameObject context should work and be implemented for // ScriptableObjects. Off the top of my head this includes, Awake, OnEnable, OnDisable, OnDestroy, // OnValidate, and Reset, but there could be more. type = unityApi.FindType("ScriptableObject"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnValidate", false, false, ApiType.Void, apiVersion, description: "This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("Reset", false, false, ApiType.Void, apiVersion, description: "Reset to default values.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } }
// Note that if we add new undocumented APIs, this won't set the correct min/max version range, and will only // apply the given api versions. That gives us two options: // 1) Recreate the api.xml file from scratch by parsing the documentation of every Unity version since 5.0 // 2) Cheat a little. When incrementally updating an existing api.xml for a single version and also adding new // undocumented APIs, add extra calls to AddUndocumentedApis with the min/max version for those new APIs. // Don't check these extra calls in! private static void AddUndocumentedApis(UnityApi unityApi, Version apiVersion) { // From AssetPostprocessingInternal var type = unityApi.FindType("AssetPostprocessor"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnPreprocessAssembly", false, false, ApiType.Void, apiVersion, undocumented: true); eventFunction.AddParameter("pathName", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); // From GitHub. Love the optimism in this one :) // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L96 var description = "This is undocumented, and a 'safeguard' for when Visual Studio gets a new release that " + "is incompatible with Unity, so that users can postprocess our csproj files to fix the issue (or " + "just completely replace them). Hopefully we'll never need this."; eventFunction = new UnityApiEventFunction("OnGeneratedCSProjectFiles", true, false, ApiType.Void, apiVersion, description, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Technically, return type is optional // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L138 description = "This callback is used by UnityVS to take over project generation from Unity"; eventFunction = new UnityApiEventFunction("OnPreGeneratingCSProjectFiles", true, false, ApiType.Bool, apiVersion, description, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // These two were added in 2018.1, as verified on GitHub // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.4/Editor/Mono/AssetPostprocessor.cs // https://github.com/Unity-Technologies/UnityCsReference/blob/2018.1/Editor/Mono/AssetPostprocessor.cs#L76 if (apiVersion >= new Version(2018, 1)) { // Technically, return type is optional // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L123 description = "This callback is used by C# code editors to modify the .csproj files."; eventFunction = new UnityApiEventFunction("OnGeneratedCSProject", true, false, ApiType.String, apiVersion, description, undocumented: true); eventFunction.AddParameter("path", ApiType.String); eventFunction.AddParameter("content", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); // Technically, return type is optional // https://github.com/Unity-Technologies/UnityCsReference/blob/96187e5fc1a23847206bf66b6f2d0e4a1ad43301/Editor/Mono/AssetPostprocessor.cs#L108 description = "This callback is used by C# code editors to modify the .sln file"; eventFunction = new UnityApiEventFunction("OnGeneratedSlnSolution", true, false, ApiType.String, apiVersion, description, undocumented: true); eventFunction.AddParameter("path", ApiType.String); eventFunction.AddParameter("content", ApiType.String); type.MergeEventFunction(eventFunction, apiVersion); } } // From AssetModificationProcessorInternal type = unityApi.FindType("AssetModificationProcessor"); if (type != null) { var eventFunction = new UnityApiEventFunction("OnStatusUpdated", true, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } type = unityApi.FindType("MonoBehaviour"); if (type != null) { const string description = "This callback is called if an associated RectTransform has its dimensions changed."; var eventFunction = new UnityApiEventFunction("OnRectTransformDimensionsChange", false, false, ApiType.Void, apiVersion, description, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnBeforeTransformParentChanged", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnDidApplyAnimationProperties", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnCanvasGroupChanged", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); eventFunction = new UnityApiEventFunction("OnCanvasHierarchyChanged", false, false, ApiType.Void, apiVersion, undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } // ScriptableObject // From Shawn White @ Unity (https://github.com/JetBrains/resharper-unity/issues/79#issuecomment-266727851): // OnValidate's behavior on ScriptableObject is the same as on MonoBehaviour. OnValidate is a non-static // method which is invoked from native and isn't picky about visibility (it'll get invoked regardless of // visibility). OnValidate is different from the other magic methods in that it only gets invoked from // the Editor. A good practice is to wrap OnValidate with #if UNITY_EDITOR so that the function can be // stripped out for deployment. // // To expand on ScriptableObject a bit. Native Unity code doesn't distinguish between MonoBehaviour and // ScriptableObject. They are the same native type (it's confusing, I know, and everyone gets tripped up // by it that looks at the native code base). The only difference is in how the object is used, // MonoBehaviours live on GameObjects, while ScriptableObjects can live alone.So in general, all magic // methods that would make sense without a GameObject context should work and be implemented for // ScriptableObjects. Off the top of my head this includes, Awake, OnEnable, OnDisable, OnDestroy, // OnValidate, and Reset, but there could be more. type = unityApi.FindType("ScriptableObject"); if (type != null && apiVersion < new Version(2020, 1)) { // Documented in 2020.1 var eventFunction = new UnityApiEventFunction("OnValidate", false, false, ApiType.Void, apiVersion, description: "This function is called when the script is loaded or a value is changed in the inspector (Called in the editor only).", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Documented in 2020.1 eventFunction = new UnityApiEventFunction("Reset", false, false, ApiType.Void, apiVersion, description: "Reset to default values.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } // TODO: Check if these event functions are available in 5.0 - 5.6 type = unityApi.FindType("Editor"); if (type != null && apiVersion >= new Version(2017, 1)) { // Editor.OnPreSceneGUI has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/SceneView/SceneView.cs#L2436 var eventFunction = new UnityApiEventFunction("OnPreSceneGUI", false, false, ApiType.Void, apiVersion, description: "Called before the Scene view is drawn.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Editor.OnSceneDrag has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/GUI/EditorCache.cs#L63 eventFunction = new UnityApiEventFunction("OnSceneDrag", false, false, ApiType.Void, apiVersion, description: "Called for each object dragged onto the scene view", undocumented: true); eventFunction.AddParameter("sceneView", new ApiType("UnityEditor.SceneView"), "The current scene view"); eventFunction.AddParameter("index", ApiType.Int, "The index into the DragAndDrop.objectReferences array"); type.MergeEventFunction(eventFunction, apiVersion); if (apiVersion < new Version(2020, 2)) { // Editor.HasFrameBounds has been around since at least 2017.1. First documented in 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/SceneView/SceneView.cs#L2296 // https://docs.unity3d.com/2020.2/Documentation/ScriptReference/Editor.HasFrameBounds.html eventFunction = new UnityApiEventFunction("HasFrameBounds", false, false, ApiType.Bool, apiVersion, description: "Validates whether custom bounds can be calculated for this editor.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // Editor.OnGetFrameBounds has been around since at least 2017.1. First documented in 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/SceneView/SceneView.cs#L2303 // https://docs.unity3d.com/2020.2/Documentation/ScriptReference/Editor.OnGetFrameBounds.html eventFunction = new UnityApiEventFunction("OnGetFrameBounds", false, false, new ApiType("UnityEngine.Bounds"), apiVersion, description: "Gets custom bounds for the target of this editor.", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } } // TODO: Check if these event functions are available in 5.0 - 5.6 type = unityApi.FindType("EditorWindow"); if (type != null && apiVersion >= new Version(2017, 1)) { // EditorWindow.ModifierKeysChanged has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/HostView.cs#L290 // http://www.improck.com/2014/11/editorwindow-modifier-keys/ var eventFunction = new UnityApiEventFunction("ModifierKeysChanged", false, false, ApiType.Void, apiVersion, description: "Called when the modifier keys are changed. Automatically registers and de-registers the EditorApplication.modifierKeysChanged event", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // EditorWindow.ShowButton has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/HostView.cs#L356 // http://www.improck.com/2014/11/editorwindow-inspector-lock-icon/ eventFunction = new UnityApiEventFunction("ShowButton", false, false, ApiType.Void, apiVersion, description: "Allow Editor panes to show a small button next to the generic menu (e.g. inspector lock icon)", undocumented: true); eventFunction.AddParameter("rect", new ApiType("UnityEngine.Rect"), "Position to draw the button"); type.MergeEventFunction(eventFunction, apiVersion); // EditorWindow.OnBecameVisible has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/HostView.cs#L302 eventFunction = new UnityApiEventFunction("OnBecameVisible", false, false, ApiType.Void, apiVersion, description: "Called when an editor window has been opened", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // EditorWindow.OnBecameInvisible has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/HostView.cs#L337 eventFunction = new UnityApiEventFunction("OnBecameInvisible", false, false, ApiType.Void, apiVersion, description: "Called when an editor window has been closed", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // EditorWindow.OnDidOpenScene has been around since at least 2017.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2017.1/Editor/Mono/HostView.cs#L163 eventFunction = new UnityApiEventFunction("OnDidOpenScene", false, false, ApiType.Void, apiVersion, description: "Called when a scene has been opened", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); if (apiVersion >= new Version(2019, 1)) { // EditorWindow.OnAddedAsTab was introduced in 2019.1. Still undocumented as of 2020.2 // https://github.com/Unity-Technologies/UnityCsReference/blob/2019.1/Editor/Mono/GUI/DockArea.cs#L188 eventFunction = new UnityApiEventFunction("OnAddedAsTab", false, false, ApiType.Void, apiVersion, description: "Called when the editor window is added as a tab", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); // EditorWindow.OnBeforeRemovedAsTab was introduced in 2019.1 // https://github.com/Unity-Technologies/UnityCsReference/blob/2019.1/Editor/Mono/GUI/DockArea.cs#L195 eventFunction = new UnityApiEventFunction("OnBeforeRemovedAsTab", false, false, ApiType.Void, apiVersion, description: "Called before an editor window is removed as a tab", undocumented: true); } if (apiVersion >= new Version(2019, 3)) { // EditorWindow.OnTabDetached was introduced in 2019.3 // https://github.com/Unity-Technologies/UnityCsReference/blob/2019.3/Editor/Mono/GUI/DockArea.cs#L940 eventFunction = new UnityApiEventFunction("OnTabDetached", false, false, ApiType.Void, apiVersion, description: "Called during drag and drop, when an editor window tab is detached", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } if (apiVersion >= new Version(2020, 1)) { // EditorWindow.OnMainWindowMove was introduced in 2020.1 // https://github.com/Unity-Technologies/UnityCsReference/blob/2020.1/Editor/Mono/HostView.cs#L343 // See comment here // https://github.com/Unity-Technologies/UnityCsReference/blob/2020.1/Editor/Mono/ExternalPlayModeView/ExternalPlayModeView.cs#L112 eventFunction = new UnityApiEventFunction("OnMainWindowMove", false, false, ApiType.Void, apiVersion, description: "Called when the main window is moved", undocumented: true); type.MergeEventFunction(eventFunction, apiVersion); } } }
private static void ParseParameters(UnityApiEventFunction eventFunction, ApiNode signature, ApiNode details, string owningMessageNamespace, string[] argumentNames) { // E.g. OnCollisionExit2D(Collision2D) - doesn't always include the argument name // Hopefully, we parsed the argument name from the example var argumentString = SigRegex.Replace(signature.Text, "$2$3"); if (string.IsNullOrWhiteSpace(argumentString)) return; var argumentStrings = argumentString.Split(',') .Select(s => s.Trim()) .ToArray(); var total = argumentStrings.Length; var arguments = argumentStrings.Select((s, i) => new Argument(s, i, total, owningMessageNamespace)).ToArray(); ResolveArguments(details, arguments, argumentNames); foreach (var argument in arguments) eventFunction.AddParameter(argument.Name, argument.Type, argument.Description); }