Ejemplo n.º 1
0
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder,
                                                                  CancellationToken token)
        {
            // GetChildren is always called with evaluation enabled (e.g. to calculate IEnumerable's "Results" node).
            // If the user has disabled "Allow property evaluation..."  we shouldn't show the "Scene Path" item.
            // Ideally, we should add it with a "Refresh" link to calculate it if required. This involves returning a
            // reference to calculate the value rather than calculating it eagerly.
            // TODO: Make "Scene Path" lazy in 212
            if (mySession.EvaluationOptions.AllowTargetInvoke)
            {
                var scenePathValue = ScenePathValueHelper.GetScenePathValue(valueRole, options, ValueServices, Logger);
                if (scenePathValue != null)
                {
                    yield return(scenePathValue);
                }
            }

            yield return(new GameObjectComponentsGroup(valueRole, ValueServices, Logger));

            yield return(new GameObjectChildrenGroup(valueRole, ValueServices, Logger));

            foreach (var valueEntity in base.GetChildren(valueRole, instanceType, options, dataHolder, token))
            {
                yield return(valueEntity);
            }
        }
Ejemplo n.º 2
0
 public override bool IsApplicable(IValueRole <TValue> role,
                                   IMetadataTypeLite instanceType,
                                   IPresentationOptions options,
                                   IUserDataHolder dataHolder)
 {
     return(myUnityOptions.ExtensionsEnabled && role.ValueReference is ExtraDetailValueReferenceDecorator <TValue>);
 }
        private bool TryCacheDebuggerDisplay(IObjectValueRole <TValue> valueRole, IMetadataTypeLite instanceType,
                                             IUserDataHolder userDataHolder)
        {
            // Special case. Replace with a second dictionary or whatever if we need to handle more types
            if (valueRole.ValueReference is NamedReferenceDecorator <TValue> reference && reference.IsNameFromValue &&
                instanceType.FullName == "UnityEngine.GameObject")
            {
                userDataHolder.PutData(ourDebuggerDisplayStringKey, GameObjectDebuggerDisplayStringWithoutName);
                return(true);
            }

            // DebuggerDisplayAttribute is inherited. This is important for applying a debug string on Behaviour, and
            // having it used in custom MonoBehaviour classes
            var current = instanceType;

            while (current != null)
            {
                if (myDebuggerDisplayValues.TryGetValue(current.GetGenericTypeDefinition().FullName,
                                                        out var displayString))
                {
                    userDataHolder.PutData(ourDebuggerDisplayStringKey, displayString);
                    return(true);
                }

                current = current.BaseType;
            }

            return(false);
        }
Ejemplo n.º 4
0
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder,
                                                                  CancellationToken token)
        {
            // GetChildren is passed options that always allow evaluation, e.g. to calculate IEnumerable's "Results"
            // node. We eagerly evaluate Scene Path here, we should return a lazy reference to allow evaluating during
            // presentation, so that we get the "Refresh" link if the user has disabled evaluation
            // TODO: Make "Scene Path" lazy in 212
            if (mySession.EvaluationOptions.AllowTargetInvoke)
            {
                // Only add "Scene Path" to the most derived type, not every "base" node back to Component
                var valueType = valueRole.ValueReference.GetValueType(options, ValueServices.ValueMetadataProvider);
                if (valueType.Equals(instanceType))
                {
                    var scenePathValue = GetGameObjectScenePath(valueRole, options);
                    if (scenePathValue != null)
                    {
                        yield return(scenePathValue);
                    }
                }
            }

            foreach (var valueEntity in base.GetChildren(valueRole, instanceType, options, dataHolder, token))
            {
                yield return(valueEntity);
            }
        }
Ejemplo n.º 5
0
 public override bool IsApplicable(IObjectValueRole <TValue> valueRole,
                                   IMetadataTypeLite instanceType,
                                   IPresentationOptions options,
                                   IUserDataHolder dataHolder)
 {
     return(myUnityOptions.ExtensionsEnabled && instanceType.Is("UnityEditor.SerializedProperty"));
 }
 protected override bool IsApplicable(IMetadataTypeLite type, IPresentationOptions options,
                                      IUserDataHolder dataHolder)
 {
     // Check debugger type proxy settings to avoid recursion while rendering Raw View. See GetChildren
     return(myUnityOptions.ExtensionsEnabled &&
            options.EvaluateDebuggerTypeProxy &&
            type.Is("UnityEditor.SerializedProperty"));
 }
 public override bool IsApplicable(IObjectValueRole <TValue> valueRole, IMetadataTypeLite instanceType,
                                   IPresentationOptions options,
                                   IUserDataHolder dataHolder)
 {
     // Note that DebuggerDisplayObjectPresenter checks options.AllowTargetInvoke here
     return(myUnityOptions.ExtensionsEnabled && options.AllowDebuggerDisplayEvaluation &&
            TryCacheDebuggerDisplay(valueRole, instanceType.GetGenericTypeDefinition(), dataHolder));
 }
Ejemplo n.º 8
0
 protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                           IMetadataTypeLite instanceType,
                                                           IPresentationOptions options,
                                                           IUserDataHolder dataHolder,
                                                           CancellationToken token)
 {
     yield return(new ChildrenGroup(valueRole, ValueServices, myLogger));
 }
 public NamedReferenceDecorator(IValueReference <TValue> originalReference, [NotNull] string name,
                                ValueOriginKind kind, [CanBeNull] IMetadataTypeLite declaredType,
                                IValueRoleFactory <TValue> roleFactory)
 {
     myOriginalReference = originalReference;
     DefaultName         = name;
     OriginKind          = kind;
     DeclaredType        = declaredType;
     myRoleFactory       = roleFactory;
 }
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder, CancellationToken token)
        {
            var references = EnumerateChildren(valueRole, options, token);

            references = FilterChildren(references);
            references = SortChildren(references);
            return(RenderChildren(valueRole, references, options, token));
        }
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder, CancellationToken token)
        {
            // GetRootGameObjects was introduced in Unity 5.3.2
            var getRootObjectsMethod = valueRole.ReifiedType.MetadataType.GetMethods()
                                       .FirstOrDefault(ourGetRootGameObjectsSelector);

            if (getRootObjectsMethod != null)
            {
                yield return(new GameObjectsGroup(valueRole, getRootObjectsMethod, ValueServices, myLogger));
            }
        }
 protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                           IMetadataTypeLite instanceType,
                                                           IPresentationOptions options,
                                                           IUserDataHolder dataHolder,
                                                           CancellationToken token)
 {
     // Keep an eye on iterators and enumeration: we need to eagerly evaluate GetChildrenImpl so we can catch any
     // exceptions. The return value of GetChildren is eagerly evaluated, so we're not changing any semantics
     return(Logger.CatchEvaluatorException <TValue, IEnumerable <IValueEntity> >(
                () => GetChildrenImpl(valueRole, instanceType, options, dataHolder, token).ToList(),
                exception =>
                Logger.LogThrownUnityException(exception, valueRole.ValueReference.OriginatingFrame,
                                               ValueServices, options))
            ?? base.GetChildren(valueRole, instanceType, options, dataHolder, token));
 }
Ejemplo n.º 13
0
        public static IMetadataTypeLite FindTypeThroughHierarchy([NotNull] this IMetadataTypeLite metadataType, string fullClrName)
        {
            var current = metadataType;

            while (current != null)
            {
                if (current.Is(fullClrName))
                {
                    return(current);
                }
                current = current.BaseType;
            }

            return(null);
        }
Ejemplo n.º 14
0
 public NamedReferenceDecorator(IValueReference <TValue> originalReference,
                                [NotNull] string name,
                                ValueOriginKind kind,
                                ValueFlags flags,
                                [CanBeNull] IMetadataTypeLite declaredType,
                                IValueRoleFactory <TValue> roleFactory,
                                bool isNameFromValue = false)
 {
     myOriginalReference = originalReference;
     DefaultName         = name;
     OriginKind          = kind;
     DefaultFlags        = flags;
     DeclaredType        = declaredType;
     myRoleFactory       = roleFactory;
     IsNameFromValue     = isNameFromValue;
 }
Ejemplo n.º 15
0
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder, CancellationToken token)
        {
            yield return(new GameObjectComponentsGroup(valueRole, ValueServices, myLogger));

            // The children of the current GameObject (as seen in Unity's Hierarchy view) are actually the children of
            // GameObject.transform. This should never be null
            var transformProperty = valueRole.GetInstancePropertyReference("transform");

            if (transformProperty != null)
            {
                yield return(new GameObjectChildrenGroup(transformProperty, ValueServices));
            }
        }
        // Return null to allow other providers a chance. If we throw EvaluatorException, it will be presented to the
        // user. OperationCancelledException will be logged and we move on to the next presenter. Any other exception
        // will leak
        public override IValuePresentation PresentValue(IObjectValueRole <TValue> valueRole,
                                                        IMetadataTypeLite instanceType,
                                                        IPresentationOptions options,
                                                        IUserDataHolder dataHolder,
                                                        CancellationToken token)
        {
            var debuggerDisplayString = dataHolder.GetData(ourDebuggerDisplayStringKey);

            try
            {
                var valueReference    = valueRole.ValueReference;
                var thisObj           = valueReference.GetValue(options);
                var evaluationOptions =
                    valueReference.OriginatingFrame.DebuggerSession.Options.EvaluationOptions.Apply(options);

                // This can throw if there are members missing, which is entirely possible when debugging on a device,
                // due to stripping. It will throw EvaluatorException. Anything else is logged and thrown as a new
                // EvaluatorException. We can also get InvalidOperationException, but only if no other evaluators can
                // handle the current context, which is unlikely
                var displayString =
                    ExpressionEvaluators.EvaluateDisplayString(valueReference.OriginatingFrame, thisObj,
                                                               debuggerDisplayString, evaluationOptions, token);

                var flags = valueReference.DefaultFlags;
                if (valueReference is CalculatedValueReferenceDecorator <TValue> reference &&
                    !reference.AllowDefaultTypePresentation)
                {
                    flags |= ValueFlags.IsDefaultTypePresentation;
                }

                return(SimplePresentation.CreateSuccess(
                           ValuePresentationPart.Default(DisplayStringUtil.EscapeString(displayString)),
                           flags, instanceType, displayString));
            }
            catch (Exception ex)
            {
                // Log as warning, not error - there's nothing the user can do, and we're likely to encounter this with
                // device builds
                myLogger.Warn(ex,
                              comment: $"Unable to evaluate debugger display string for type {instanceType.GetGenericTypeDefinition().FullName}: {debuggerDisplayString}. " +
                              "Expected behaviour on devices due to stripping");
                return(null);
            }
        }
Ejemplo n.º 17
0
        public override IValuePresentation PresentValue(IValueRole <TValue> valueRole,
                                                        IMetadataTypeLite instanceType,
                                                        IPresentationOptions options,
                                                        IUserDataHolder dataHolder,
                                                        CancellationToken token)
        {
            var extraDetail  = (ExtraDetailValueReferenceDecorator <TValue>)valueRole.ValueReference;
            var presentation = extraDetail.UnderlyingValueReference.ToValue(ValueServices).GetValuePresentation(options, token);

            if (presentation.ResultKind == ValueResultKind.Success)
            {
                var presentationBuilder = PresentationBuilder.New(presentation.Value.ToArray())
                                          .Add(ValuePresentationPart.Space)
                                          .SpecialSymbol("(").Default(extraDetail.ExtraDetail).SpecialSymbol(")");
                return(SimplePresentation.CreateSuccess(presentationBuilder.Result(), presentation.Flags,
                                                        presentation.Type, presentation.PrimitiveValue));
            }

            return(presentation);
        }
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder, CancellationToken token)
        {
            var scenePathValue = ScenePathValueHelper.GetScenePathValue(valueRole, options, ValueServices, myLogger);

            if (scenePathValue != null)
            {
                yield return(scenePathValue);
            }

            yield return(new GameObjectComponentsGroup(valueRole, ValueServices, myLogger));

            yield return(new GameObjectChildrenGroup(valueRole, ValueServices, myLogger));

            foreach (var valueEntity in base.GetChildren(valueRole, instanceType, options, dataHolder, token))
            {
                yield return(valueEntity);
            }
        }
Ejemplo n.º 19
0
        protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole,
                                                                  IMetadataTypeLite instanceType,
                                                                  IPresentationOptions options,
                                                                  IUserDataHolder dataHolder, CancellationToken token)
        {
            // Only add "Scene Path" to the most derived type, not every "base" node back to Component
            var valueType = valueRole.ValueReference.GetValueType(options, ValueServices.ValueMetadataProvider);

            if (valueType.Equals(instanceType))
            {
                var scenePathValue = GetGameObjectScenePath(valueRole, options);
                if (scenePathValue != null)
                {
                    yield return(scenePathValue);
                }
            }

            foreach (var valueEntity in base.GetChildren(valueRole, instanceType, options, dataHolder, token))
            {
                yield return(valueEntity);
            }
        }
        public override IValuePresentation PresentValue(IStringValueRole <TValue> valueRole, IMetadataTypeLite instanceType,
                                                        IPresentationOptions options, IUserDataHolder dataHolder, CancellationToken token)
        {
            // Present the value's string as plain text, without any syntax highlighting or quote handling. Don't use
            // ValueFlags.IsString, as it will add an unnecessary "View" link - our text is always short
            var text = valueRole.GetString();

            return(SimplePresentation.CreateSuccess(ValuePresentationPart.Default(text),
                                                    valueRole.ValueReference.DefaultFlags | ValueFlags.NoChildren, instanceType, text));
        }
Ejemplo n.º 21
0
 protected override bool IsApplicable(IMetadataTypeLite type, IPresentationOptions options,
                                      IUserDataHolder dataHolder)
 {
     return(type.Is("UnityEngine.GameObject"));
 }
Ejemplo n.º 22
0
 protected override bool IsApplicable(IMetadataTypeLite type, IPresentationOptions options,
                                      IUserDataHolder dataHolder)
 {
     // UnityEngine.SceneManagement.Scene was introduced in Unity 5.3
     return(type.Is("UnityEngine.SceneManagement.Scene"));
 }
Ejemplo n.º 23
0
 protected override IEnumerable <IValueEntity> GetChildren(IObjectValueRole <TValue> valueRole, IMetadataTypeLite instanceType, IPresentationOptions options,
                                                           IUserDataHolder dataHolder, CancellationToken token)
 {
     return(options.FlattenHierarchy
         ? EnumerateMembersFlat(valueRole, options, token, ValueServices)
         : EnumerateMembersWithBaseNode(valueRole, options, token, ValueServices));
 }
Ejemplo n.º 24
0
 protected override bool IsApplicable(IMetadataTypeLite type, IPresentationOptions options,
                                      IUserDataHolder dataHolder)
 {
     return(myUnityOptions.ExtensionsEnabled && type.FindTypeThroughHierarchy("UnityEngine.Component") != null);
 }
Ejemplo n.º 25
0
 protected override bool IsApplicable(IObjectValueRole <TValue> role, IMetadataTypeLite type,
                                      IPresentationOptions options, IUserDataHolder dataHolder)
 {
     return(myUnityOptions.ExtensionsEnabled && type.Is("UnityEditor.SerializedObject"));
 }
        private IEnumerable <IValueEntity> GetChildrenImpl(IObjectValueRole <TValue> valueRole,
                                                           IMetadataTypeLite instanceType,
                                                           IPresentationOptions options,
                                                           IUserDataHolder dataHolder,
                                                           CancellationToken token)
        {
            var enumValueObject = valueRole.GetInstancePropertyReference("propertyType")
                                  ?.AsObjectSafe(options)?.GetEnumValue(options);
            var propertyType = (SerializedPropertyKind)Enum.ToObject(typeof(SerializedPropertyKind),
                                                                     enumValueObject ?? SerializedPropertyKind.Invalid);

            // Fall back to showing everything if we don't have any special handling for the property type. This should
            // protect us if Unity introduces a new serialised property type.
            if (!myHandledPropertyTypes.Contains(propertyType))
            {
                foreach (var valueEntity in base.GetChildren(valueRole, instanceType, options, dataHolder, token))
                {
                    yield return(valueEntity);
                }
                yield break;
            }

            var propertySpecificFields = myPerTypeFieldNames[propertyType];

            var isArray       = false;
            var isFixedBuffer = false;

            // Generic means a custom serializable struct, an array or a fixed buffer (public unsafe fixed int buf[10])
            // Strings are also arrays, and have properties for each character. We'll show them too.
            if (propertyType == SerializedPropertyKind.Generic || propertyType == SerializedPropertyKind.String)
            {
                if (!Util.TryEvaluatePrimitiveProperty(valueRole, "isArray", options, out isArray) || !isArray)
                {
                    Util.TryEvaluatePrimitiveProperty(valueRole, "isFixedBuffer", options, out isFixedBuffer);
                }
            }

            // We filter all non-public members, so make sure we don't show the group
            var effectiveOptions = options.WithOverridden(o => o.GroupPrivateMembers = false);

            var references = EnumerateChildren(valueRole, effectiveOptions, token);

            references = FilterChildren(references, propertySpecificFields, isArray, isFixedBuffer);
            references = DecorateChildren(valueRole, references, propertyType, options);
            references = SortChildren(references);
            foreach (var valueEntity in RenderChildren(valueRole, references, effectiveOptions, token))
            {
                yield return(valueEntity);
            }

            if (!Util.TryEvaluatePrimitiveProperty(valueRole, "hasChildren", options, out bool hasChildren))
            {
                Logger.Warn("Cannot evaluate hasChildren for serializedProperty");
            }
            else if (hasChildren)
            {
                // Arrays, fixed buffer arrays and strings (which are arrays) all say they have children. They don't.
                // They have a special sibling (i.e. same depth) that can only be enumerated with Next(true), and is
                // skipped with Next(false).
                // We don't show these special siblings, because they're not children, and the data is already displayed
                // as part of the array/fixed buffer element group.
                // TODO: Should we show this?
                // It might be confusing if these special properties are never shown, especially if someone is using
                // the debugger to try to figure out the shape of the serialised stream. The question is, how do we show
                // something that is enumerated as a child, but stored as a sibling? The best solution is a dump of the
                // whole serialised stream, which is not what we're doing as part of the debugger. One solution would be
                // to add an "Array" node that is shown instead of "Children" - but that would still look like a child
                // node, and would only include the "Size" node over the existing array/fixed buffer element group.
                // For now, just ignore these special properties and leave it to the array/fixed buffer element group.
                if (!isArray && !isFixedBuffer && propertyType != SerializedPropertyKind.String)
                {
                    yield return(new ChildrenGroup(valueRole, ValueServices, Logger));
                }
            }

            if (isArray)
            {
                if (!Util.TryEvaluatePrimitiveProperty(valueRole, "arraySize", options, out int arraySize))
                {
                    Logger.Warn("Cannot evaluate arraySize for serializedProperty");
                }
                else if (arraySize > 0)
                {
                    yield return(new ArrayElementsGroup(valueRole, arraySize,
                                                        MethodSelectors.SerializedProperty_GetArrayElementAtIndex, ValueServices, Logger));
                }
            }
            else if (isFixedBuffer)
            {
                if (!Util.TryEvaluatePrimitiveProperty(valueRole, "fixedBufferSize", options, out int fixedBufferSize))
                {
                    Logger.Warn("Cannot evaluate fixedBufferSize for serializedProperty");
                }
                else if (fixedBufferSize > 0)
                {
                    yield return(new ArrayElementsGroup(valueRole, fixedBufferSize,
                                                        MethodSelectors.SerializedProperty_GetFixedBufferElementAtIndex, ValueServices, Logger));
                }
            }

            // Disable debugger type proxy options to avoid recursion. See IsApplicable.
            var rawViewOptions = options.WithOverridden(o => o.EvaluateDebuggerTypeProxy = false);

            yield return(new SimpleEntityGroup(PresentationOptions.RawViewGroupName,
                                               valueRole.ValueReference.ToValue(ValueServices).GetChildren(rawViewOptions, token)));
        }
Ejemplo n.º 27
0
        public override IValuePresentation PresentValue(IStringValueRole <TValue> valueRole, IMetadataTypeLite instanceType,
                                                        IPresentationOptions options, IUserDataHolder dataHolder, CancellationToken token)
        {
            // Note that ValueFlags.IsString will add the "View" link to see a string in a popup. We don't need this
            var text = valueRole.GetString();

            return(SimplePresentation.CreateSuccess(ValuePresentationPart.Default(text),
                                                    valueRole.ValueReference.DefaultFlags | ValueFlags.NoChildren, instanceType, text));
        }
Ejemplo n.º 28
0
        public override IValuePresentation PresentValue(IObjectValueRole <TValue> valueRole,
                                                        IMetadataTypeLite instanceType,
                                                        IPresentationOptions options, IUserDataHolder dataHolder,
                                                        CancellationToken token)
        {
            var showName     = (valueRole.ValueReference as CalculatedValueReferenceDecorator <TValue>)?.AllowNameInValue ?? true;
            var showTypeName = (valueRole.ValueReference as CalculatedValueReferenceDecorator <TValue>)
                               ?.AllowDefaultTypePresentation ?? true;

            var nameValuePresentation = valueRole.GetInstancePropertyReference("name")?.ToValue(ValueServices)
                                        ?.GetValuePresentation(options);
            var propertyTypeReference         = valueRole.GetInstancePropertyReference("propertyType");
            var propertyTypeValuePresentation = propertyTypeReference?.ToValue(ValueServices)?.GetValuePresentation(options);

            var propertyTypeEnumValueObject = propertyTypeReference?.AsObjectSafe(options)?.GetEnumValue(options);
            var propertyType = (SerializedPropertyKind)Enum.ToObject(typeof(SerializedPropertyKind),
                                                                     propertyTypeEnumValueObject ?? SerializedPropertyKind.Generic);

            int?   arraySize        = null;
            string arrayElementType = null;
            string genericType      = null;

            if (propertyType == SerializedPropertyKind.Generic)
            {
                if (Util.TryEvaluatePrimitiveProperty(valueRole, "isArray", options, out bool isArray) && isArray)
                {
                    arraySize = valueRole.GetInstancePropertyReference("arraySize")?.AsPrimitiveSafe(options)
                                ?.GetPrimitive <int>();
                    arrayElementType =
                        valueRole.GetInstancePropertyReference("arrayElementType")?.AsStringSafe(options)?.GetString();
                }
                else if (Util.TryEvaluatePrimitiveProperty(valueRole, "isFixedBuffer", options,
                                                           out bool isFixedBuffer) &&
                         isFixedBuffer)
                {
                    arraySize = valueRole.GetInstancePropertyReference("fixedBufferSize")?.AsPrimitiveSafe(options)
                                ?.GetPrimitive <int>();
                    arrayElementType =
                        valueRole.GetInstancePropertyReference("arrayElementType")?.AsStringSafe(options)?.GetString();
                }
                else
                {
                    genericType = valueRole.GetInstancePropertyReference("type")?.AsStringSafe(options)?.GetString();
                }
            }

            var valuePresentation = GetValuePresentation(valueRole, propertyType, options, out var extraDetail);

            var parts = PresentationBuilder.New();

            parts.OpenBrace();

            if (showName && nameValuePresentation != null)
            {
                parts.Comment("name: ").Add(nameValuePresentation.Value.ToArray())
                .Add(ValuePresentationPart.Space);
            }

            parts.Comment("propertyType: ");
            if (propertyType == SerializedPropertyKind.Generic && arraySize != null && arrayElementType != null)
            {
                parts.Default($"{arrayElementType}[{arraySize}]");
            }
            else if (genericType != null)
            {
                parts.Default(genericType);
            }
            else if (propertyTypeValuePresentation != null)
            {
                parts.Add(propertyTypeValuePresentation.Value.ToArray());
            }
            else
            {
                parts.Comment("(Unknown)");
            }

            if (valuePresentation != null)
            {
                parts.Add(ValuePresentationPart.Space)
                .Comment("value: ").Add(valuePresentation.Value.ToArray());
                if (!string.IsNullOrEmpty(extraDetail))
                {
                    parts.Add(ValuePresentationPart.Space).SpecialSymbol("(").Default(extraDetail).SpecialSymbol(")");
                }
            }

            parts.ClosedBrace();

            // Hide the default type presentation if we've been asked to
            var flags = !showTypeName ? ValueFlags.IsDefaultTypePresentation : 0;

            return(SimplePresentation.Create(parts.Result(), ValueResultKind.Success, ValueFlags.None | flags,
                                             instanceType));
        }