< Summary

Line coverage
64%
Covered lines: 317
Uncovered lines: 173
Coverable lines: 490
Total lines: 941
Line coverage: 64.6%
Branch coverage
58%
Covered branches: 84
Total branches: 144
Branch coverage: 58.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: .cctor()100%11100%
File 1: Initialize(...)30%101088.6%
File 1: IsValidDeclaration(...)62.5%8876.92%
File 1: Get(...)66.66%121284.61%
File 1: GetCallbackFlags(...)50%2266.66%
File 1: GetValueCallbackFlags(Microsoft.CodeAnalysis.AttributeData&,System.Threading.CancellationToken&)87.5%161694.44%
File 1: GetCallbackName(...)12.5%8837.5%
File 2: GenerateCode(...)58.33%121256.75%
File 2: WriteElements(...)100%11100%
File 2: WriteSubscribedBitMask(...)100%22100%
File 2: WriteSubscribedEnumMask(...)100%88100%
File 2: WriteCachedFields(...)0%220%
File 2: WriteField(Hertzole.ScriptableValues.Generator.CodeWriter,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.CallbackData&)0%12120%
File 2: WriteSubscribeAndUnsubscribeMethods(...)75%161688.57%
File 2: WriteIfCheck(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.CallbackData&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,System.Boolean)100%66100%
File 2: WriteCallbacksMethods(...)83.33%6694.73%
File 2: WriteCallbackMethod(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.CallbackData&,System.ReadOnlySpan`1<System.Char>&,System.ReadOnlySpan`1<System.ValueTuple`2<System.String,System.String>>&,System.ReadOnlySpan`1<System.ValueTuple`2<System.String,System.String>>&)100%66100%
File 2: AppendSubscribeMaskFieldName(...)50%2275%
File 2: AppendSubscribeMaskTypeName(...)50%2275%
File 3: WriteValueField(...)100%110%
File 3: WriteEventField(...)0%220%
File 3: WritePoolField(...)100%110%
File 3: WriteCollectionField(...)100%110%
File 3: WriteCachedField(...)0%880%
File 3: WriteGeneratedCodeAttribute(...)100%44100%

File(s)

Generator/Hertzole.ScriptableValues.Generator/Generator/ScriptableCallbackGenerator.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Collections.Immutable;
 3using System.Threading;
 4using Microsoft.CodeAnalysis;
 5using Microsoft.CodeAnalysis.CSharp;
 6using Microsoft.CodeAnalysis.CSharp.Syntax;
 7
 8namespace Hertzole.ScriptableValues.Generator;
 9
 10[Generator(LanguageNames.CSharp)]
 11public sealed partial class ScriptableCallbackGenerator : IIncrementalGenerator
 12{
 113    internal static readonly string generatorName = "Hertzole.ScriptableValues.Generator." + nameof(ScriptableCallbackGe
 114    internal static readonly string generatorVersion = typeof(ScriptableCallbackGenerator).Assembly.GetName().Version.To
 15
 16    /// <inheritdoc />
 17    public void Initialize(IncrementalGeneratorInitializationContext context)
 18    {
 19        Log.Info<ScriptableCallbackGenerator>("Initialized");
 20
 2721        IncrementalValueProvider<ImmutableArray<(HierarchyInfo, CallbackData)>> values =
 2722            context.SyntaxProvider
 2723                   .ForAttributeWithMetadataName(
 2724                       "Hertzole.ScriptableValues.GenerateValueCallbackAttribute",
 2725                       IsValidDeclaration,
 2526                       (syntaxContext, token) => Get(in syntaxContext, CallbackType.Value, in token))
 2527                   .SelectMany(static (array, _) => array)
 2528                   .Where(static x => x != default)
 2729                   .Collect();
 30
 2731        IncrementalValueProvider<ImmutableArray<(HierarchyInfo, CallbackData)>> events =
 2732            context.SyntaxProvider
 2733                   .ForAttributeWithMetadataName(
 2734                       "Hertzole.ScriptableValues.GenerateEventCallbackAttribute",
 2735                       IsValidDeclaration,
 036                       (syntaxContext, token) => Get(in syntaxContext, CallbackType.Event, in token))
 037                   .SelectMany(static (array, _) => array)
 038                   .Where(static x => x != default)
 2739                   .Collect();
 40
 2741        IncrementalValueProvider<ImmutableArray<(HierarchyInfo, CallbackData)>> collections =
 2742            context.SyntaxProvider
 2743                   .ForAttributeWithMetadataName(
 2744                       "Hertzole.ScriptableValues.GenerateCollectionCallbackAttribute",
 2745                       IsValidDeclaration,
 046                       (syntaxContext, token) => Get(in syntaxContext, CallbackType.Collection, in token))
 047                   .SelectMany(static (array, _) => array)
 048                   .Where(static x => x != default)
 2749                   .Collect();
 50
 2751        IncrementalValueProvider<ImmutableArray<(HierarchyInfo, CallbackData)>> pools =
 2752            context.SyntaxProvider
 2753                   .ForAttributeWithMetadataName(
 2754                       "Hertzole.ScriptableValues.GeneratePoolCallbackAttribute",
 2755                       IsValidDeclaration,
 056                       (syntaxContext, token) => Get(in syntaxContext, CallbackType.Pool, in token))
 057                   .SelectMany(static (array, _) => array)
 058                   .Where(static x => x != default)
 2759                   .Collect();
 60
 2761        IncrementalValuesProvider<(HierarchyInfo Key, EquatableArray<CallbackData> Elements)> allCallbacks = values.Comb
 2762            .Combine(pools)
 2763            .SelectMany(static (tuple, token) =>
 2764            {
 2765                Dictionary<HierarchyInfo, ArrayBuilder<CallbackData>> map = new Dictionary<HierarchyInfo, ArrayBuilder<C
 2766
 2767                AddToMap(map, tuple.Left.Left.Left, token);
 2768                AddToMap(map, tuple.Left.Left.Right, token);
 2769                AddToMap(map, tuple.Left.Right, token);
 2770                AddToMap(map, tuple.Right, token);
 2771
 2772                token.ThrowIfCancellationRequested();
 2773
 2774                ImmutableArray<(HierarchyInfo Key, EquatableArray<CallbackData> Elements)>.Builder result =
 2775                    ImmutableArray.CreateBuilder<(HierarchyInfo, EquatableArray<CallbackData>)>();
 2776
 10477                foreach (KeyValuePair<HierarchyInfo, ArrayBuilder<CallbackData>> entry in map)
 2778                {
 2579                    result.Add((entry.Key, entry.Value.ToImmutable()));
 2780                }
 2781
 2782                return result;
 2783
 2784                static void AddToMap(Dictionary<HierarchyInfo, ArrayBuilder<CallbackData>> map,
 2785                    in ImmutableArray<(HierarchyInfo, CallbackData)> array,
 2786                    CancellationToken cancellationToken)
 2787                {
 26688                    foreach ((HierarchyInfo key, CallbackData value) in array)
 2789                    {
 2590                        cancellationToken.ThrowIfCancellationRequested();
 2791
 2592                        if (!map.TryGetValue(key, out ArrayBuilder<CallbackData> builder))
 2793                        {
 2594                            builder = new ArrayBuilder<CallbackData>();
 2595                            map.Add(key, builder);
 2796                        }
 2797
 2598                        builder.Add(value);
 2799                    }
 108100                }
 27101            });
 102
 27103        context.RegisterImplementationSourceOutput(allCallbacks, GenerateCode);
 27104    }
 105
 106    private static bool IsValidDeclaration(SyntaxNode node, CancellationToken cancellationToken)
 107    {
 27108        SyntaxNode targetNode = node;
 27109        if (node.TryGetFieldDeclaration(out FieldDeclarationSyntax? fieldNode))
 110        {
 15111            targetNode = fieldNode!;
 112        }
 12113        else if (!node.IsKind(SyntaxKind.PropertyDeclaration))
 114        {
 0115            return false;
 116        }
 117
 27118        cancellationToken.ThrowIfCancellationRequested();
 119
 120        // Cast the node to a MemberDeclarationSyntax to check for attributes.
 27121        MemberDeclarationSyntax memberNode = (MemberDeclarationSyntax) targetNode;
 122
 123        // Must have at least one attribute.
 27124        if (memberNode.AttributeLists.Count == 0)
 125        {
 0126            return false;
 127        }
 128
 27129        cancellationToken.ThrowIfCancellationRequested();
 130
 131        // We must be inside a type.
 27132        if (targetNode.Parent is not TypeDeclarationSyntax parentTypeNode)
 133        {
 0134            return false;
 135        }
 136
 137        // Parent must have at least one attribute, usually the GenerateScriptableCallbacks attribute.
 27138        return parentTypeNode.AttributeLists.Count > 0;
 139    }
 140
 141    private static ImmutableArray<(HierarchyInfo, CallbackData)> Get(in GeneratorAttributeSyntaxContext context,
 142        in CallbackType callbackType,
 143        in CancellationToken cancellationToken)
 144    {
 25145        cancellationToken.ThrowIfCancellationRequested();
 146
 147        // Check if the marker attribute was found.
 25148        if (!context.TargetSymbol.ContainingType.HasAttribute(Types.GLOBAL_MARKER_ATTRIBUTE))
 149        {
 0150            return ImmutableArray<(HierarchyInfo, CallbackData)>.Empty;
 151        }
 152
 25153        cancellationToken.ThrowIfCancellationRequested();
 154
 155        ITypeSymbol memberType;
 25156        switch (context.TargetSymbol)
 157        {
 158            case IFieldSymbol fieldSymbol:
 13159                memberType = fieldSymbol.Type;
 13160                break;
 161            case IPropertySymbol propertySymbol:
 12162                memberType = propertySymbol.Type;
 12163                break;
 164            default:
 0165                return ImmutableArray<(HierarchyInfo, CallbackData)>.Empty;
 166        }
 167
 168        // Just a safety check to make sure we have a valid type.
 25169        if (memberType is not INamedTypeSymbol namedMemberType)
 170        {
 0171            return ImmutableArray<(HierarchyInfo, CallbackData)>.Empty;
 172        }
 173
 25174        cancellationToken.ThrowIfCancellationRequested();
 175
 176        // Try to get the scriptable type from the member type.
 25177        if (!ScriptableValueHelper.TryGetScriptableType(context.SemanticModel, namedMemberType, out ScriptableType scrip
 25178                out ITypeSymbol? genericType))
 179        {
 0180            return ImmutableArray<(HierarchyInfo, CallbackData)>.Empty;
 181        }
 182
 25183        HierarchyInfo hierarchy = HierarchyInfo.FromSymbol(context.TargetSymbol.ContainingType);
 184
 25185        using ArrayBuilder<(HierarchyInfo, CallbackData)> builder = new ArrayBuilder<(HierarchyInfo, CallbackData)>(cont
 186
 100187        for (int i = 0; i < context.Attributes.Length; i++)
 188        {
 25189            cancellationToken.ThrowIfCancellationRequested();
 190
 25191            CallbackFlags callbackFlags = GetCallbackFlags(in callbackType, context.Attributes[i], in cancellationToken)
 25192            string callbackName = GetCallbackName(context.Attributes[i], in cancellationToken);
 193
 25194            builder.Add((
 25195                hierarchy, new CallbackData(context.TargetSymbol.Name, callbackType, callbackFlags, memberType, scriptab
 196        }
 197
 25198        return builder.ToImmutable();
 25199    }
 200
 201    private static CallbackFlags GetCallbackFlags(in CallbackType callbackType,
 202        in AttributeData attribute,
 203        in CancellationToken cancellationToken)
 204    {
 25205        switch (callbackType)
 206        {
 207            case CallbackType.Value:
 25208                return GetValueCallbackFlags(in attribute, in cancellationToken);
 209            default:
 0210                return CallbackFlags.None;
 211        }
 212
 213        static CallbackFlags GetValueCallbackFlags(in AttributeData attribute, in CancellationToken cancellationToken)
 214        {
 25215            if (attribute.AttributeClass == null ||
 25216                attribute.AttributeClass.ToDisplayString() != "Hertzole.ScriptableValues.GenerateValueCallbackAttribute"
 217            {
 0218                return CallbackFlags.None;
 219            }
 220
 25221            cancellationToken.ThrowIfCancellationRequested();
 222
 25223            CallbackFlags flags = CallbackFlags.None;
 224
 25225            bool hasChanged = false;
 25226            bool hasChanging = false;
 227
 228            // No arguments mean it's a OnXChanged
 25229            if (attribute.ConstructorArguments.Length == 0)
 230            {
 9231                hasChanged = true;
 232            }
 16233            else if (attribute.ConstructorArguments.Length > 0)
 234            {
 235                // If the argument is 0, it's a OnXChanging.
 16236                if (attribute.ConstructorArguments[0].Value is int value && value == 0)
 237                {
 8238                    hasChanging = true;
 239                }
 240                else
 241                {
 8242                    hasChanged = true;
 243                }
 244            }
 245
 25246            if (hasChanging)
 247            {
 8248                flags |= CallbackFlags.PreInvoke;
 249            }
 250
 25251            if (hasChanged)
 252            {
 17253                flags |= CallbackFlags.PostInvoke;
 254            }
 255
 25256            return flags;
 257        }
 258    }
 259
 260    private static string GetCallbackName(AttributeData attribute, in CancellationToken cancellationToken)
 261    {
 25262        cancellationToken.ThrowIfCancellationRequested();
 263
 25264        if (attribute.NamedArguments.IsDefaultOrEmpty)
 265        {
 25266            return string.Empty;
 267        }
 268
 0269        foreach (KeyValuePair<string, TypedConstant> namedArgument in attribute.NamedArguments)
 270        {
 0271            cancellationToken.ThrowIfCancellationRequested();
 272
 0273            if (namedArgument.Key == "CallbackName" && namedArgument.Value.Value is string name)
 274            {
 0275                return name;
 276            }
 277        }
 278
 0279        return string.Empty;
 280    }
 281}

Generator/Hertzole.ScriptableValues.Generator/Generator/ScriptableCallbackGenerator.Execute.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Text;
 4using System.Threading;
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.Text;
 7using SourceProductionContext = Microsoft.CodeAnalysis.SourceProductionContext;
 8using SymbolDisplayFormat = Microsoft.CodeAnalysis.SymbolDisplayFormat;
 9
 10namespace Hertzole.ScriptableValues.Generator;
 11
 12partial class ScriptableCallbackGenerator
 13{
 14    private static void GenerateCode(SourceProductionContext context, (HierarchyInfo Key, EquatableArray<CallbackData> E
 15    {
 2516        if (!ScriptableValueHelper.DoesTypeSupportCallbacks((ITypeSymbol) item.Key.Symbol, out _))
 17        {
 118            return;
 19        }
 20
 2421        CodeWriter writer = new CodeWriter();
 2422        CancellationToken cancellationToken = context.CancellationToken;
 23
 24        try
 25        {
 2426            writer.AppendFileHeader();
 2427            writer.AppendNamespace(item.Key.Namespace);
 28
 2429            int nestLevel = 0;
 2430            INamedTypeSymbol? containingType = item.Key.Symbol.ContainingType;
 2431            while (containingType != null && !containingType.IsNamespace)
 32            {
 033                writer.Append("partial  ");
 034                writer.Append(containingType.IsValueType ? "struct" : "class");
 035                writer.AppendLine(containingType.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat));
 036                writer.AppendLine("{");
 037                writer.Indent++;
 38
 039                nestLevel++;
 040                containingType = containingType.ContainingType;
 41            }
 42
 2443            writer.Append("partial ");
 44
 2445            writer.Append(item.Key.IsStruct ? "struct " : "class ");
 46
 2447            writer.AppendLine(item.Key.TypeName);
 2448            writer.AppendLine("{");
 2449            writer.Indent++;
 50
 2451            WriteElements(writer, in item.Key, in item.Elements, in cancellationToken);
 52
 2453            writer.Indent--;
 2454            writer.Append("}");
 55
 4856            for (int i = 0; i < nestLevel; i++)
 57            {
 058                writer.AppendLine();
 059                writer.Indent--;
 060                writer.Append("}");
 61            }
 2462        }
 063        catch (Exception e)
 64        {
 065            writer.Clear();
 066            writer.AppendLine("// Error generating code:");
 067            writer.AppendLine("// " + e.Message);
 068            writer.AppendLine("// " + e.StackTrace);
 069        }
 70
 2471        context.AddSource($"{item.Key.FilenameHint}.g.cs", SourceText.From(writer.ToString(), Encoding.UTF8));
 2472    }
 73
 74    private static void WriteElements(CodeWriter writer,
 75        in HierarchyInfo hierarchy,
 76        in EquatableArray<CallbackData> elements,
 77        in CancellationToken cancellationToken)
 78    {
 2479        WriteSubscribedBitMask(writer, in hierarchy, in elements, in cancellationToken);
 2480        writer.AppendLine();
 81
 2482        WriteSubscribeAndUnsubscribeMethods(in writer, in hierarchy, in elements, in cancellationToken);
 2483        writer.AppendLine();
 84
 2485        WriteCallbacksMethods(in writer, in elements, in cancellationToken);
 2486    }
 87
 88    private static void WriteSubscribedBitMask(CodeWriter writer,
 89        in HierarchyInfo hierarchy,
 90        in EquatableArray<CallbackData> data,
 91        in CancellationToken cancellationToken)
 92    {
 2493        cancellationToken.ThrowIfCancellationRequested();
 94
 2495        writer.AppendLine(DocumentationHelper.SUBSCRIBED_MASK_SUMMARY);
 2496        using (writer.WithIndent(0))
 97        {
 2498            writer.AppendLine("#if UNITY_EDITOR");
 2499        }
 100
 24101        writer.AppendLine("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableS
 24102        WriteGeneratedCodeAttribute(writer);
 24103        using (writer.WithIndent(0))
 104        {
 24105            writer.AppendLine("#endif // UNITY_EDITOR");
 24106        }
 107
 24108        WriteSubscribedEnumMask(in writer, in data, in hierarchy, in cancellationToken);
 109
 24110        writer.AppendLine();
 24111        writer.AppendLine("/// <summary>The current mask of all subscribed callbacks.</summary>");
 24112        using (writer.WithIndent(0))
 113        {
 24114            writer.AppendLine("#if UNITY_EDITOR");
 24115        }
 116
 24117        writer.AppendLine("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableS
 24118        WriteGeneratedCodeAttribute(writer);
 24119        using (writer.WithIndent(0))
 120        {
 24121            writer.AppendLine("#endif // UNITY_EDITOR");
 24122        }
 123
 24124        writer.Append("private ");
 24125        AppendSubscribeMaskTypeName(in writer, in hierarchy);
 24126        writer.Append(' ');
 24127        AppendSubscribeMaskFieldName(in writer, in hierarchy);
 128
 24129        if (!hierarchy.IsStruct)
 130        {
 12131            writer.Append(" = ");
 12132            AppendSubscribeMaskTypeName(in writer, in hierarchy);
 12133            writer.Append(".None");
 134        }
 135
 24136        writer.AppendLine(";");
 24137    }
 138
 139    internal static void WriteSubscribedEnumMask(in CodeWriter writer,
 140        in EquatableArray<CallbackData> data,
 141        in HierarchyInfo hierarchy,
 142        in CancellationToken cancellationToken)
 143    {
 35144        cancellationToken.ThrowIfCancellationRequested();
 145
 35146        writer.Append("private enum ");
 35147        AppendSubscribeMaskTypeName(in writer, in hierarchy);
 35148        writer.Append(" : ");
 35149        if (data.Length <= 8)
 150        {
 26151            writer.AppendLine("byte");
 152        }
 9153        else if (data.Length <= 16)
 154        {
 3155            writer.AppendLine("ushort");
 156        }
 6157        else if (data.Length <= 32)
 158        {
 3159            writer.AppendLine("uint");
 160        }
 161        else
 162        {
 3163            writer.AppendLine("ulong");
 164        }
 165
 35166        writer.AppendLine("{");
 35167        writer.Indent++;
 168
 35169        writer.Append("None = 0");
 170
 708171        for (int i = 0; i < data.Length; i++)
 172        {
 319173            cancellationToken.ThrowIfCancellationRequested();
 174
 319175            writer.AppendLine(",");
 319176            writer.Append(data[i].MaskName);
 319177            writer.Append(" = 1 << ");
 319178            writer.Append(i);
 179        }
 180
 35181        writer.AppendLine();
 182
 35183        writer.Indent--;
 35184        writer.AppendLine("}");
 35185    }
 186
 187    private static void WriteCachedFields(CodeWriter writer,
 188        in SourceProductionContext context,
 189        in HierarchyInfo hierarchy,
 190        in EquatableArray<CallbackData> data)
 191    {
 0192        for (int i = 0; i < data.Length; i++)
 193        {
 0194            context.CancellationToken.ThrowIfCancellationRequested();
 195
 0196            WriteField(writer, in hierarchy, in data[i]);
 197        }
 198
 0199        return;
 200
 201        static void WriteField(CodeWriter writer, in HierarchyInfo hierarchy, in CallbackData data)
 202        {
 0203            writer.Append("/// <summary>Cached callback for when <see cref=\"");
 0204            writer.Append(data.Name);
 0205            writer.Append("\" /> is ");
 0206            if (data.CallbackType == CallbackType.Event)
 207            {
 0208                writer.Append("invoked");
 209            }
 0210            else if (data.CallbackType == CallbackType.Value && (data.Flags & CallbackFlags.PreInvoke) != 0)
 211            {
 0212                writer.Append("changing");
 213            }
 214            else
 215            {
 0216                writer.Append("changed");
 217            }
 218
 0219            writer.AppendLine(".</summary>");
 220
 0221            switch (data.CallbackType)
 222            {
 223                case CallbackType.Value:
 0224                    WriteValueField(in writer, in hierarchy, in data);
 0225                    break;
 226                case CallbackType.Event:
 0227                    WriteEventField(in writer, in hierarchy, in data);
 0228                    break;
 229                case CallbackType.Pool:
 0230                    WritePoolField(in writer, in hierarchy, in data);
 0231                    break;
 232                case CallbackType.Collection:
 0233                    WriteCollectionField(in writer, in hierarchy, in data);
 234                    break;
 235            }
 0236        }
 237    }
 238
 239    private static void WriteSubscribeAndUnsubscribeMethods(in CodeWriter writer,
 240        in HierarchyInfo hierarchy,
 241        in EquatableArray<CallbackData> elements,
 242        in CancellationToken cancellationToken)
 243    {
 24244        cancellationToken.ThrowIfCancellationRequested();
 245
 24246        writer.AppendLine("/// <summary>Subscribes to all scriptable callbacks.</summary>");
 247
 24248        WriteGeneratedCodeAttribute(in writer, true);
 24249        writer.AppendExcludeFromCodeCoverageAttribute();
 250
 251        // If it should be inherited, it should be protected override.
 252        // If it should not be inherited, and is sealed, it should be private.
 253        // If it should not be inherited, and is not sealed, it should be protected virtual.
 24254        if (hierarchy.ShouldInherit)
 255        {
 0256            writer.Append("protected override ");
 257        }
 258        else
 259        {
 24260            writer.Append(hierarchy.IsSealed ? "private " : "protected virtual ");
 261        }
 262
 24263        writer.AppendLine("void SubscribeToAllScriptableCallbacks()");
 24264        writer.AppendLine("{");
 24265        writer.Indent++;
 266
 24267        if (hierarchy.ShouldInherit)
 268        {
 0269            writer.AppendLine("base.SubscribeToAllScriptableCallbacks();");
 270        }
 271
 96272        for (int i = 0; i < elements.Length; i++)
 273        {
 24274            cancellationToken.ThrowIfCancellationRequested();
 24275            WriteIfCheck(in writer, in elements[i], in hierarchy, true);
 276        }
 277
 24278        writer.Indent--;
 24279        writer.AppendLine("}");
 24280        writer.AppendLine();
 281
 24282        writer.AppendLine("/// <summary>Unsubscribes from all scriptable callbacks.</summary>");
 283
 24284        WriteGeneratedCodeAttribute(in writer, true);
 24285        writer.AppendExcludeFromCodeCoverageAttribute();
 286
 287        // If it should be inherited, it should be protected override.
 288        // If it should not be inherited, and is sealed, it should be private.
 289        // If it should not be inherited, and is not sealed, it should be protected virtual.
 24290        if (hierarchy.ShouldInherit)
 291        {
 0292            writer.Append("protected override ");
 293        }
 294        else
 295        {
 24296            writer.Append(hierarchy.IsSealed ? "private " : "protected virtual ");
 297        }
 298
 24299        writer.AppendLine("void UnsubscribeFromAllScriptableCallbacks()");
 24300        writer.AppendLine("{");
 24301        writer.Indent++;
 302
 24303        if (hierarchy.ShouldInherit)
 304        {
 0305            writer.AppendLine("base.UnsubscribeFromAllScriptableCallbacks();");
 306        }
 307
 96308        for (int i = 0; i < elements.Length; i++)
 309        {
 24310            cancellationToken.ThrowIfCancellationRequested();
 24311            WriteIfCheck(in writer, in elements[i], in hierarchy, false);
 312        }
 313
 24314        writer.Indent--;
 24315        writer.AppendLine("}");
 24316        return;
 317
 318        static void WriteIfCheck(in CodeWriter writer,
 319            in CallbackData data,
 320            in HierarchyInfo hierarchy,
 321            bool subscribe)
 322        {
 48323            writer.Append("if ((");
 48324            AppendSubscribeMaskFieldName(in writer, in hierarchy);
 48325            writer.Append(" & ");
 48326            AppendSubscribeMaskTypeName(in writer, in hierarchy);
 48327            writer.Append('.');
 48328            writer.Append(data.MaskName);
 48329            writer.Append(") ");
 48330            writer.Append(subscribe ? "== 0" : "!= 0");
 48331            writer.AppendLine(")");
 48332            writer.AppendLine("{");
 48333            writer.Indent++;
 334
 48335            writer.Append(data.Name);
 48336            writer.Append('.');
 48337            writer.Append(data.TargetEvent);
 48338            writer.Append(subscribe ? " += " : " -= ");
 48339            writer.Append(data.CallbackName);
 48340            writer.AppendLine(";");
 341
 48342            AppendSubscribeMaskFieldName(in writer, in hierarchy);
 343
 48344            writer.Append(subscribe ? " |= " : " &= ~");
 48345            AppendSubscribeMaskTypeName(in writer, in hierarchy);
 48346            writer.Append('.');
 347
 48348            writer.Append(data.MaskName);
 48349            writer.AppendLine(";");
 350
 48351            writer.Indent--;
 48352            writer.AppendLine("}");
 48353        }
 354    }
 355
 356    private static void WriteCallbacksMethods(in CodeWriter writer, in EquatableArray<CallbackData> elements, in Cancell
 357    {
 24358        ArrayBuilder<(string name, string type)> parametersBuilder = new ArrayBuilder<(string name, string type)>(2);
 24359        ArrayBuilder<(string name, string description)> descriptionsBuilder = new ArrayBuilder<(string name, string desc
 24360        HashSet<string> usedCallbacks = new HashSet<string>(StringComparer.Ordinal);
 361
 362        try
 363        {
 96364            for (int i = 0; i < elements.Length; i++)
 365            {
 24366                cancellationToken.ThrowIfCancellationRequested();
 367
 24368                if (usedCallbacks.Contains(elements[i].CallbackName))
 369                {
 370                    continue;
 371                }
 372
 24373                parametersBuilder.Clear();
 24374                descriptionsBuilder.Clear();
 375
 24376                if (i > 0)
 377                {
 0378                    writer.AppendLine();
 379                }
 380
 24381                elements[i].AppendParameterTypes(in parametersBuilder);
 24382                elements[i].AppendParameterDescriptions(in descriptionsBuilder);
 383
 24384                WriteCallbackMethod(in writer, in elements[i], elements[i].CallbackName.AsSpan(), parametersBuilder.AsSp
 24385                usedCallbacks.Add(elements[i].CallbackName);
 386            }
 24387        }
 388        finally
 389        {
 24390            parametersBuilder.Dispose();
 24391            descriptionsBuilder.Dispose();
 24392        }
 393
 24394        return;
 395
 396        static void WriteCallbackMethod(in CodeWriter writer,
 397            in CallbackData data,
 398            in ReadOnlySpan<char> name,
 399            in ReadOnlySpan<(string name, string type)> parameters,
 400            in ReadOnlySpan<(string argName, string description)> parameterDescriptions)
 401        {
 24402            writer.AppendLine(DocumentationHelper.GetMethodCallbackDescription(data.Name.AsSpan(), data.CallbackType, da
 403
 144404            for (int i = 0; i < parameterDescriptions.Length; i++)
 405            {
 48406                writer.Append("/// <param name=\"");
 48407                writer.Append(parameterDescriptions[i].argName);
 48408                writer.Append("\">");
 48409                writer.Append(parameterDescriptions[i].description);
 48410                writer.AppendLine("</param>");
 411            }
 412
 24413            writer.Append("private partial void ");
 24414            writer.Append(name);
 24415            writer.Append("(");
 416
 144417            for (int i = 0; i < parameters.Length; i++)
 418            {
 48419                if (i > 0)
 420                {
 24421                    writer.Append(", ");
 422                }
 423
 48424                writer.Append(parameters[i].type);
 48425                writer.Append(' ');
 48426                writer.Append(parameters[i].name);
 427            }
 428
 24429            writer.AppendLine(");");
 24430        }
 431    }
 432
 433    internal static void AppendSubscribeMaskFieldName(in CodeWriter builder, in HierarchyInfo hierarchy)
 434    {
 120435        builder.Append("subscribedCallbacks");
 436
 120437        if (hierarchy.ShouldInherit)
 438        {
 0439            builder.Append(hierarchy.ParentCount);
 440        }
 120441    }
 442
 443    internal static void AppendSubscribeMaskTypeName(in CodeWriter builder, in HierarchyInfo hierarchy)
 444    {
 167445        builder.Append("SubscribedCallbacksMask");
 446
 167447        if (hierarchy.ShouldInherit)
 448        {
 0449            builder.Append(hierarchy.ParentCount);
 450        }
 167451    }
 452}

Generator/Hertzole.ScriptableValues.Generator/Generator/ScriptableCallbackGenerator.Writing.cs

#LineLine coverage
 1using System;
 2using SymbolDisplayFormat = Microsoft.CodeAnalysis.SymbolDisplayFormat;
 3
 4namespace Hertzole.ScriptableValues.Generator;
 5
 6partial class ScriptableCallbackGenerator
 7{
 8    private static void WriteValueField(in CodeWriter writer, in HierarchyInfo hierarchy, in CallbackData data)
 9    {
 010        string genericTypeName = data.GenericType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
 11
 012        ArrayBuilder<string> genericParameters = new ArrayBuilder<string>(3);
 013        ArrayBuilder<string> callbackParameters = new ArrayBuilder<string>(3);
 014        ArrayBuilder<char> callback = new ArrayBuilder<char>(64);
 15
 16        try
 17        {
 018            genericParameters.Add(genericTypeName);
 019            genericParameters.Add(genericTypeName);
 020            genericParameters.Add(hierarchy.Symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 21
 022            callbackParameters.Add("oldValue");
 023            callbackParameters.Add("newValue");
 024            callbackParameters.Add("context");
 25
 026            callback.AddRange("context.");
 027            callback.AddRange(data.CallbackName);
 028            callback.AddRange("(oldValue, newValue);");
 29
 030            WriteCachedField(in writer, "global::System.Action", genericParameters.AsSpan(), data.CachedFieldName.AsSpan
 031                callback.AsSpan());
 032        }
 33        finally
 34        {
 35            // Dispose manually instead of using 'using' to avoid creating a bunch of try blocks.
 036            genericParameters.Dispose();
 037            callbackParameters.Dispose();
 038            callback.Dispose();
 039        }
 040    }
 41
 42    private static void WriteEventField(in CodeWriter writer, in HierarchyInfo hierarchy, in CallbackData data)
 43    {
 044        ArrayBuilder<string> genericParameters = new ArrayBuilder<string>(3);
 045        ArrayBuilder<string> callbackParameters = new ArrayBuilder<string>(3);
 046        ArrayBuilder<char> callback = new ArrayBuilder<char>(64);
 47
 48        try
 49        {
 50            // If it's a generic event, write the generic type.
 051            if (data.ScriptableType == ScriptableType.GenericEvent)
 52            {
 053                genericParameters.Add(data.GenericType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 54            }
 55
 056            genericParameters.Add(hierarchy.Symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 57
 058            callbackParameters.Add("sender");
 059            callbackParameters.Add("args");
 060            callbackParameters.Add("context");
 61
 062            callback.AddRange("context.");
 063            callback.AddRange(data.CallbackName);
 064            callback.AddRange("(sender, args);");
 65
 066            WriteCachedField(in writer, "global::Hertzole.ScriptableValues.EventHandlerWithContext", genericParameters.A
 067                callbackParameters.AsSpan(), callback.AsSpan());
 068        }
 69        finally
 70        {
 071            genericParameters.Dispose();
 072            callbackParameters.Dispose();
 073            callback.Dispose();
 074        }
 075    }
 76
 77    private static void WritePoolField(in CodeWriter writer, in HierarchyInfo hierarchy, in CallbackData data)
 78    {
 079        ArrayBuilder<string> genericParameters = new ArrayBuilder<string>(3);
 080        ArrayBuilder<string> callbackParameters = new ArrayBuilder<string>(3);
 081        ArrayBuilder<char> callback = new ArrayBuilder<char>(64);
 82
 83        try
 84        {
 085            genericParameters.Add(data.GenericType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 086            genericParameters.Add(hierarchy.Symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 87
 088            callbackParameters.Add("action");
 089            callbackParameters.Add("item");
 090            callbackParameters.Add("context");
 91
 092            callback.AddRange("context.");
 093            callback.AddRange(data.CallbackName);
 094            callback.AddRange("(action, item);");
 95
 096            WriteCachedField(in writer, "global::Hertzole.ScriptableValues.PoolEventArgsWithContext", genericParameters.
 097                callbackParameters.AsSpan(), callback.AsSpan());
 098        }
 99        finally
 100        {
 0101            genericParameters.Dispose();
 0102            callbackParameters.Dispose();
 0103            callback.Dispose();
 0104        }
 0105    }
 106
 107    private static void WriteCollectionField(in CodeWriter writer, in HierarchyInfo hierarchy, in CallbackData data)
 108    {
 0109        ArrayBuilder<string> genericParameters = new ArrayBuilder<string>(3);
 0110        ArrayBuilder<string> callbackParameters = new ArrayBuilder<string>(3);
 0111        ArrayBuilder<char> callback = new ArrayBuilder<char>(64);
 112
 113        try
 114        {
 0115            genericParameters.Add(data.GenericType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 0116            genericParameters.Add(hierarchy.Symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
 117
 0118            callbackParameters.Add("args");
 0119            callbackParameters.Add("context");
 120
 0121            callback.AddRange("context.");
 0122            callback.AddRange(data.CallbackName);
 0123            callback.AddRange("(args);");
 124
 0125            WriteCachedField(in writer, "global::Hertzole.ScriptableValues.CollectionChangedWithContextEventHandler", ge
 0126                data.CachedFieldName.AsSpan(),
 0127                callbackParameters.AsSpan(), callback.AsSpan());
 0128        }
 129        finally
 130        {
 0131            genericParameters.Dispose();
 0132            callbackParameters.Dispose();
 0133            callback.Dispose();
 0134        }
 0135    }
 136
 137    private static void WriteCachedField(in CodeWriter writer,
 138        string action,
 139        in ReadOnlySpan<string> genericParameters,
 140        in ReadOnlySpan<char> name,
 141        in ReadOnlySpan<string> callbackParameters,
 142        in ReadOnlySpan<char> callback)
 143    {
 0144        using (writer.WithIndent(0))
 145        {
 0146            writer.AppendLine("#if UNITY_EDITOR");
 0147        }
 148
 0149        writer.AppendLine("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableS
 0150        WriteGeneratedCodeAttribute(writer);
 0151        using (writer.WithIndent(0))
 152        {
 0153            writer.AppendLine("#endif // UNITY_EDITOR");
 0154        }
 155
 0156        writer.Append("private static ");
 0157        writer.Append(action);
 0158        writer.Append("<");
 0159        for (int i = 0; i < genericParameters.Length; i++)
 160        {
 0161            if (i > 0)
 162            {
 0163                writer.Append(", ");
 164            }
 165
 0166            writer.Append(genericParameters[i]);
 167        }
 168
 0169        writer.Append("> ");
 0170        writer.Append(name);
 0171        writer.Append(" = (");
 172
 0173        for (int i = 0; i < callbackParameters.Length; i++)
 174        {
 0175            if (i > 0)
 176            {
 0177                writer.Append(", ");
 178            }
 179
 0180            writer.Append(callbackParameters[i]);
 181        }
 182
 0183        writer.Append(") => { ");
 0184        writer.Append(callback);
 0185        writer.AppendLine(" };");
 0186    }
 187
 188    private static void WriteGeneratedCodeAttribute(in CodeWriter writer, bool withIfDefines = false)
 189    {
 96190        if (withIfDefines)
 191        {
 48192            using (writer.WithIndent(0))
 193            {
 48194                writer.AppendLine("#if UNITY_EDITOR");
 48195            }
 196        }
 197
 96198        writer.AppendGeneratedCodeAttribute(generatorName, generatorVersion);
 199
 96200        if (withIfDefines)
 201        {
 48202            using (writer.WithIndent(0))
 203            {
 48204                writer.AppendLine("#endif // UNITY_EDITOR");
 48205            }
 206        }
 96207    }
 208}

Methods/Properties

.cctor()
Initialize(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext)
IsValidDeclaration(Microsoft.CodeAnalysis.SyntaxNode,System.Threading.CancellationToken)
Get(Microsoft.CodeAnalysis.GeneratorAttributeSyntaxContext&,Hertzole.ScriptableValues.Generator.CallbackType&,System.Threading.CancellationToken&)
GetCallbackFlags(Hertzole.ScriptableValues.Generator.CallbackType&,Microsoft.CodeAnalysis.AttributeData&,System.Threading.CancellationToken&)
GetValueCallbackFlags(Microsoft.CodeAnalysis.AttributeData&,System.Threading.CancellationToken&)
GetCallbackName(Microsoft.CodeAnalysis.AttributeData,System.Threading.CancellationToken&)
GenerateCode(Microsoft.CodeAnalysis.SourceProductionContext,System.ValueTuple`2<Hertzole.ScriptableValues.Generator.HierarchyInfo,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>>)
WriteElements(Hertzole.ScriptableValues.Generator.CodeWriter,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>&,System.Threading.CancellationToken&)
WriteSubscribedBitMask(Hertzole.ScriptableValues.Generator.CodeWriter,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>&,System.Threading.CancellationToken&)
WriteSubscribedEnumMask(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,System.Threading.CancellationToken&)
WriteCachedFields(Hertzole.ScriptableValues.Generator.CodeWriter,Microsoft.CodeAnalysis.SourceProductionContext&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>&)
WriteField(Hertzole.ScriptableValues.Generator.CodeWriter,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.CallbackData&)
WriteSubscribeAndUnsubscribeMethods(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>&,System.Threading.CancellationToken&)
WriteIfCheck(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.CallbackData&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,System.Boolean)
WriteCallbacksMethods(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.EquatableArray`1<Hertzole.ScriptableValues.Generator.CallbackData>&,System.Threading.CancellationToken&)
WriteCallbackMethod(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.CallbackData&,System.ReadOnlySpan`1<System.Char>&,System.ReadOnlySpan`1<System.ValueTuple`2<System.String,System.String>>&,System.ReadOnlySpan`1<System.ValueTuple`2<System.String,System.String>>&)
AppendSubscribeMaskFieldName(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&)
AppendSubscribeMaskTypeName(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&)
WriteValueField(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.CallbackData&)
WriteEventField(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.CallbackData&)
WritePoolField(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.CallbackData&)
WriteCollectionField(Hertzole.ScriptableValues.Generator.CodeWriter&,Hertzole.ScriptableValues.Generator.HierarchyInfo&,Hertzole.ScriptableValues.Generator.CallbackData&)
WriteCachedField(Hertzole.ScriptableValues.Generator.CodeWriter&,System.String,System.ReadOnlySpan`1<System.String>&,System.ReadOnlySpan`1<System.Char>&,System.ReadOnlySpan`1<System.String>&,System.ReadOnlySpan`1<System.Char>&)
WriteGeneratedCodeAttribute(Hertzole.ScriptableValues.Generator.CodeWriter&,System.Boolean)