public override void Populate(CSharpGeneratorContext context) { if (!context.Project.IsUnityProject()) { return; } if (!(context.ClassDeclaration.DeclaredElement is IClass typeElement)) { return; } var unityVersion = myUnityVersion.GetActualVersion(context.Project); var eventFunctions = myUnityApi.GetEventFunctions(typeElement, unityVersion) .Where(f => typeElement.Methods.All(m => f.Match(m) == EventFunctionMatch.NoMatch)).ToArray(); var classDeclaration = context.ClassDeclaration; var factory = CSharpElementFactory.GetInstance(classDeclaration); var methods = eventFunctions .Select(e => e.CreateDeclaration(factory, classDeclaration)) .Select(d => d.DeclaredElement) .Where(m => m != null); // Make sure we only add a method once (e.g. EditorWindow derives from ScriptableObject // and both declare the OnDestroy event function) var elements = methods.Select(m => new GeneratorDeclaredElement <IMethod>(m)) .Distinct(m => m.TestDescriptor); context.ProvidedElements.AddRange(elements); }
public override void Populate(CSharpGeneratorContext context) { if (!context.Project.IsUnityProject()) { return; } if (!(context.ClassDeclaration.DeclaredElement is IClass typeElement)) { return; } // CompactOneToListMap is optimised for the typical use case of only one item per key var existingMethods = new CompactOneToListMap <string, IMethod>(); foreach (var typeMemberInstance in typeElement.GetAllClassMembers <IMethod>()) { existingMethods.AddValue(typeMemberInstance.Member.ShortName, typeMemberInstance.Member); } var groupingTypeLookup = new Dictionary <IClrTypeName, ITypeElement>(); var factory = CSharpElementFactory.GetInstance(context.ClassDeclaration); var elements = new List <GeneratorDeclaredElement>(); var unityVersion = myUnityVersion.GetActualVersion(context.Project); var eventFunctions = myUnityApi.GetEventFunctions(typeElement, unityVersion); foreach (var eventFunction in eventFunctions.OrderBy(e => e.Name, new UnityEventFunctionComparer())) { // Note that we handle grouping, but it's off by default, and Rider doesn't save and restore the last // used grouping value. We can set EnforceGrouping, but that's a bit too much // https://youtrack.jetbrains.com/issue/RIDER-25194 if (!groupingTypeLookup.TryGetValue(eventFunction.TypeName, out var groupingType)) { groupingType = myKnownTypesCache.GetByClrTypeName(eventFunction.TypeName, context.PsiModule) .GetTypeElement(); groupingTypeLookup.Add(eventFunction.TypeName, groupingType); } var makeVirtual = false; var accessRights = AccessRights.PRIVATE; var exactMatch = existingMethods[eventFunction.Name] .FirstOrDefault(m => eventFunction.Match(m) == MethodSignatureMatch.ExactMatch); if (exactMatch != null) { // Exact match. Only offer to implement if it's virtual and in a base class if (!exactMatch.IsVirtual) { continue; } var containingType = exactMatch.GetContainingType(); if (Equals(containingType, typeElement)) { continue; } makeVirtual = true; accessRights = exactMatch.GetAccessRights(); groupingType = containingType; } var newMethodDeclaration = eventFunction .CreateDeclaration(factory, myKnownTypesCache, context.ClassDeclaration, accessRights, makeVirtual); if (makeVirtual) { // Make the parameter names are the same as the overridden method, or the "redundant override" // inspection doesn't kick in var overrideParameters = exactMatch.Parameters; var newParameters = newMethodDeclaration.ParameterDeclarations; for (var i = 0; i < overrideParameters.Count; i++) { newParameters[i].SetName(overrideParameters[i].ShortName); } } var newMethod = newMethodDeclaration.DeclaredElement; Assertion.AssertNotNull(newMethod, "newMethod != null"); elements.Add(new GeneratorDeclaredElement(newMethod, newMethod.IdSubstitution, groupingType)); } context.ProvidedElements.AddRange(elements.Distinct(m => m.TestDescriptor)); }