/// <summary> /// Formats <paramref name="enumerable"/> as an array. /// </summary> /// <param name="enumerable"></param> /// <param name="formatWriter"></param> /// <param name="recursionLevel"></param> /// <param name="lineage"></param> /// <returns>The max <paramref name="recursionLevel"/> reached when <paramref name="enumerable"/> was formatted.</returns> private int FormatAsEnumerable(IEnumerable enumerable, FormatWriter formatWriter, int recursionLevel, Stack <object> lineage) { int maxRecursionLevel = recursionLevel; bool isEmpty = true; bool enumerableContainsAllPrimitives = true; // Determine if there are any non-primitive elements foreach (object element in enumerable) { isEmpty = false; if (!ShouldFormatAsPrimitive(element)) { enumerableContainsAllPrimitives = false; break; } } if (isEmpty) { formatWriter.WriteField("[]", ColorCategory.Markup); return(recursionLevel); } else { formatWriter.WriteField("[", ColorCategory.Markup); formatWriter.IndentLevel += 1; bool onFirstElement = true; foreach (object element in enumerable) { if (!onFirstElement) { formatWriter.WriteText(",", ColorCategory.Markup); } if (!enumerableContainsAllPrimitives) { formatWriter.WriteEndLine(); } onFirstElement = false; // Format as a sub-object (indented, on a newline) int propertyMaxRecursionLevel = InnerFormatObject(element, formatWriter, recursionLevel + 1, lineage, null); maxRecursionLevel = Math.Max(maxRecursionLevel, propertyMaxRecursionLevel); } formatWriter.IndentLevel -= 1; if (!enumerableContainsAllPrimitives) { formatWriter.WriteEndLine(); } formatWriter.WriteField("]", ColorCategory.Markup); } return(maxRecursionLevel); }
/// <summary> /// Formats <paramref name="untypedDictionary"/> as dictionary with key type <typeparamref name="TKey"/> and value type <typeparamref name="TValue"/>. /// </summary> /// <param name="untypedDictionary"></param> /// <param name="formatWriter"></param> /// <param name="recursionLevel"></param> /// <param name="lineage"></param> /// <param name="parentPropertyType">The type of the property set to object <paramref name="untypedDictionary"/>. May be <c>null</c> if not applicable or unknown.</param> /// <returns>The max <paramref name="recursionLevel"/> reached when <paramref name="untypedDictionary"/> was formatted.</returns> // ReSharper disable once UnusedMember.Local private int FormatAsDictionary <TKey, TValue>(object untypedDictionary, FormatWriter formatWriter, int recursionLevel, Stack <object> lineage, Type parentPropertyType) { // OK that this throws on failure; this shouldn't happen var dictionary = (IEnumerable <KeyValuePair <TKey, TValue> >)untypedDictionary; int maxRecursionLevel = recursionLevel; if (!dictionary.Any()) { formatWriter.WriteField("{}", ColorCategory.Markup); return(recursionLevel); } else { formatWriter.WriteField("{", ColorCategory.Markup); formatWriter.IndentLevel += 1; bool onFirstKvp = true; Type dictionaryType = dictionary.GetType(); if (IncludeTypeNames && (parentPropertyType != dictionaryType)) { formatWriter.WriteField("type:", ColorCategory.Debug); formatWriter.WriteText(_typeNameFunc(dictionaryType), ColorCategory.Debug); onFirstKvp = false; } foreach (var kvp in dictionary) { if (!onFirstKvp) { formatWriter.WriteText(",", ColorCategory.Markup); } formatWriter.WriteEndLine(); onFirstKvp = false; formatWriter.WriteField("{", ColorCategory.Markup); formatWriter.IndentLevel += 1; // If key is primitive, format on single line TKey key = kvp.Key; TValue value = kvp.Value; int childMaxRecursionLevel; if (ShouldFormatAsPrimitive(key)) { formatWriter.WriteText(formatWriter.FieldDelimiter, ColorCategory.Markup); FormatAsPrimitive(key, formatWriter); formatWriter.WriteText(":", ColorCategory.Markup); childMaxRecursionLevel = InnerFormatObject(value, formatWriter, recursionLevel + 1, lineage, typeof(TValue)); } else { formatWriter.WriteField("Key", ColorCategory.Detail); formatWriter.WriteText(":", ColorCategory.Markup); childMaxRecursionLevel = InnerFormatObject(key, formatWriter, recursionLevel + 1, lineage, typeof(TKey)); maxRecursionLevel = Math.Max(maxRecursionLevel, childMaxRecursionLevel); formatWriter.WriteText(",", ColorCategory.Markup); formatWriter.WriteEndLine(); formatWriter.WriteField("Value", ColorCategory.Detail); formatWriter.WriteText(":", ColorCategory.Markup); childMaxRecursionLevel = InnerFormatObject(value, formatWriter, recursionLevel + 1, lineage, typeof(TValue)); } maxRecursionLevel = Math.Max(maxRecursionLevel, childMaxRecursionLevel); if (!ShouldFormatAsPrimitive(value)) { formatWriter.WriteLine(); } formatWriter.IndentLevel -= 1; formatWriter.WriteField("}", ColorCategory.Markup); } formatWriter.WriteEndLine(); formatWriter.IndentLevel -= 1; formatWriter.WriteField("}", ColorCategory.Markup); } return(maxRecursionLevel); }
/// <summary> /// Formats <paramref name="o"/> as an object. /// </summary> /// <param name="o"></param> /// <param name="formatWriter"></param> /// <param name="recursionLevel"></param> /// <param name="lineage"></param> /// <param name="parentPropertyType">The type of the property set to value <paramref name="o"/>. May be <c>null</c> if not applicable or unknown.</param> /// <returns>The max <paramref name="recursionLevel"/> reached when <paramref name="o"/> was formatted.</returns> private int FormatAsObject(object o, FormatWriter formatWriter, int recursionLevel, Stack <object> lineage, Type parentPropertyType) { int maxRecursionLevel = recursionLevel; formatWriter.WriteField("{", ColorCategory.Markup); if (recursionLevel > 0) { formatWriter.WriteEndLine(); } formatWriter.IndentLevel += 1; Type objectType = o.GetType(); if (IncludeTypeNames && (parentPropertyType != objectType)) { formatWriter.WriteField("type:", ColorCategory.Debug); formatWriter.WriteText(_typeNameFunc(objectType), ColorCategory.Debug); formatWriter.WriteLine(); } // Write properties foreach (var property in objectType.GetReadablePublicProperties()) { bool colonWritten = false; void WriteErrorAsPropertyValue(string errorMessage) { if (!colonWritten) { formatWriter.WriteText(":", ColorCategory.Markup); } formatWriter.WriteText("!(", ColorCategory.Markup); formatWriter.WriteText(errorMessage, ColorCategory.Warning); formatWriter.WriteText(")!", ColorCategory.Markup); } try { formatWriter.WriteField(property.Name, ColorCategory.Detail); Type propertyType = property.PropertyType; ParameterInfo[] propertyIndexParameters = property.GetIndexParameters(); if (propertyIndexParameters.Length > 0) { // Properties with index parameters are not formatted - they act like functions, it's not possible to enumerate all contained values. WriteErrorAsPropertyValue("Properties with index parameters are not formatted"); continue; } // GetValue(object) throws if the property has index parameters object propertyValue = property.GetValue(o); if (IncludeTypeNames) { // Only write the propertyType if it doesn't equal the subobject type bool writePropertyType = !object.ReferenceEquals(propertyValue?.GetType(), propertyType); if (writePropertyType) { formatWriter.WriteText("(", ColorCategory.Markup); formatWriter.WriteText(_typeNameFunc(propertyType), ColorCategory.Debug); formatWriter.WriteText(")", ColorCategory.Markup); } } formatWriter.WriteText(":", ColorCategory.Markup); colonWritten = true; int propertyMaxRecursionLevel = InnerFormatObject(propertyValue, formatWriter, recursionLevel + 1, lineage, propertyType); maxRecursionLevel = Math.Max(maxRecursionLevel, propertyMaxRecursionLevel); } catch (Exception excp) { // Exception reading or formatting the property value. WriteErrorAsPropertyValue(excp.ToString()); } } // No newline for simple objects with no sub-objects if (maxRecursionLevel > 1) { formatWriter.WriteEndLine(); } formatWriter.IndentLevel -= 1; formatWriter.WriteField("}", ColorCategory.Markup); return(maxRecursionLevel); }