Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/DendroDocs.Client/DendroDocs.Client.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>

<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
namespace DendroDocs.Extensions;

/// <summary>
/// Provides extension methods for working with collections of attribute descriptions.
/// </summary>
public static class IEnumerableIAttributeDescriptionExtensions
{
/// <summary>
/// Filters the collection to return only attribute descriptions of the specified type.
/// </summary>
/// <param name="list">The collection of attribute descriptions to filter.</param>
/// <param name="fullname">The full type name of the attribute to match.</param>
/// <returns>A read-only list containing only the attribute descriptions of the specified type.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="list"/> is <c>null</c>.</exception>
public static IReadOnlyList<AttributeDescription> OfType(this IEnumerable<AttributeDescription> list, string fullname)
{
ArgumentNullException.ThrowIfNull(list);

return [.. list.Where(ad => string.Equals(ad.Type, fullname, StringComparison.Ordinal))];
}

/// <summary>
/// Determines whether the collection contains an attribute of the specified type.
/// </summary>
/// <param name="list">The collection of attribute descriptions to check.</param>
/// <param name="fullname">The full type name of the attribute to search for.</param>
/// <returns><c>true</c> if an attribute of the specified type is found; otherwise, <c>false</c>.</returns>
public static bool HasAttribute(this IEnumerable<AttributeDescription> list, string fullname)
{
return list.OfType(fullname).Any();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
namespace DendroDocs.Extensions;

/// <summary>
/// Provides extension methods for working with collections of method descriptions.
/// </summary>
public static class IEnumerableMethodDescriptionExtensions
{
/// <summary>
/// Filters the collection to return only method descriptions with the specified name.
/// </summary>
/// <param name="list">The collection of method descriptions to filter.</param>
/// <param name="name">The method name to match.</param>
/// <returns>A read-only list containing only the method descriptions with the specified name.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="list"/> is <c>null</c>.</exception>
public static IReadOnlyList<MethodDescription> WithName(this IEnumerable<MethodDescription> list, string name)
{
ArgumentNullException.ThrowIfNull(list);
Expand Down
10 changes: 10 additions & 0 deletions src/DendroDocs.Client/Extensions/IEnumerableStringExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
namespace DendroDocs.Extensions;

/// <summary>
/// Provides extension methods for working with collections of string values.
/// </summary>
public static class IEnumerableStringExtensions
{
/// <summary>
/// Filters the collection to return only strings that start with the specified prefix.
/// </summary>
/// <param name="list">The collection of strings to filter.</param>
/// <param name="partialName">The prefix to match against.</param>
/// <returns>A read-only list containing only the strings that start with the specified prefix.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="list"/> or <paramref name="partialName"/> is <c>null</c>.</exception>
public static IReadOnlyList<string> StartsWith(this IEnumerable<string> list, string partialName)
{
ArgumentNullException.ThrowIfNull(list);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
namespace DendroDocs.Extensions;

/// <summary>
/// Provides extension methods for working with collections of type descriptions, including type lookup,
/// inheritance analysis, method invocation tracing, and member population.
/// </summary>
public static class IEnumerableTypeDescriptionExtensions
{
/// <summary>
/// Finds the first type description with the specified full name.
/// </summary>
/// <param name="types">The collection of type descriptions to search.</param>
/// <param name="typeName">The full name of the type to find.</param>
/// <returns>The first type description with the specified name.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
/// <exception cref="InvalidOperationException">Thrown when no type with the specified name is found.</exception>
public static TypeDescription First(this IEnumerable<TypeDescription> types, string typeName)
{
ArgumentNullException.ThrowIfNull(types);

return types.First(t => string.Equals(t.FullName, typeName, StringComparison.Ordinal));
}

/// <summary>
/// Finds the first type description with the specified full name, or returns <c>null</c> if not found.
/// </summary>
/// <param name="types">The collection of type descriptions to search.</param>
/// <param name="typeName">The full name of the type to find.</param>
/// <returns>The first type description with the specified name, or <c>null</c> if not found.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
public static TypeDescription? FirstOrDefault(this IEnumerable<TypeDescription> types, string typeName)
{
ArgumentNullException.ThrowIfNull(types);

return types.FirstOrDefault(t => string.Equals(t.FullName, typeName, StringComparison.Ordinal));
}

/// <summary>
/// Finds method bodies that match the specified method invocation.
/// </summary>
/// <param name="types">The collection of type descriptions to search.</param>
/// <param name="invocation">The method invocation to match against.</param>
/// <returns>A read-only list of method bodies that match the invocation. Returns an empty list if no matches are found.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
public static IReadOnlyList<IHaveAMethodBody> GetInvokedMethod(this IEnumerable<TypeDescription> types, InvocationDescription invocation)
{
ArgumentNullException.ThrowIfNull(types);
Expand All @@ -29,6 +55,13 @@ public static IReadOnlyList<IHaveAMethodBody> GetInvokedMethod(this IEnumerable<
return [.. type.MethodBodies().Where(invocation.MatchesMethod)];
}

/// <summary>
/// Recursively traces all method invocations that result from executing the specified invocation.
/// </summary>
/// <param name="types">The collection of type descriptions to search.</param>
/// <param name="invocation">The initial method invocation to trace.</param>
/// <returns>A read-only list of all invocations in the call chain, including the original invocation.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
public static IReadOnlyList<InvocationDescription> GetInvocationConsequences(this IEnumerable<TypeDescription> types, InvocationDescription invocation)
{
ArgumentNullException.ThrowIfNull(types);
Expand All @@ -42,6 +75,13 @@ public static IReadOnlyList<InvocationDescription> GetInvocationConsequences(thi
return consequences;
}

/// <summary>
/// Gets all statements that result from executing the specified method invocation, including nested statements from called methods.
/// </summary>
/// <param name="types">The collection of type descriptions to search.</param>
/// <param name="invocation">The method invocation to analyze.</param>
/// <returns>A read-only list of all statements that result from the invocation, including the invocation itself.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
public static IReadOnlyList<Statement> GetInvocationConsequenceStatements(this IEnumerable<TypeDescription> types, InvocationDescription invocation)
{
ArgumentNullException.ThrowIfNull(types);
Expand All @@ -55,6 +95,13 @@ public static IReadOnlyList<Statement> GetInvocationConsequenceStatements(this I
return consequences;
}

/// <summary>
/// Recursively traverses and expands complex statements (like switches and conditional statements) to include all nested statements.
/// </summary>
/// <param name="types">The collection of type descriptions to use for method resolution.</param>
/// <param name="sourceStatement">The statement to traverse and expand.</param>
/// <returns>A read-only list of statements with expanded nested structures. For simple statements, returns an empty list.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
public static IReadOnlyList<Statement> TraverseStatement(this IEnumerable<TypeDescription> types, Statement sourceStatement)
{
ArgumentNullException.ThrowIfNull(types);
Expand Down Expand Up @@ -107,6 +154,12 @@ public static IReadOnlyList<Statement> TraverseStatement(this IEnumerable<TypeDe
}
}

/// <summary>
/// Populates the inheritance hierarchy for all types by adding inherited base types recursively.
/// This method modifies the BaseTypes collection of each type to include all inherited types from the inheritance chain.
/// </summary>
/// <param name="types">The collection of type descriptions to process.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
public static void PopulateInheritedBaseTypes(this IEnumerable<TypeDescription> types)
{
ArgumentNullException.ThrowIfNull(types);
Expand Down Expand Up @@ -141,6 +194,15 @@ private static void PopulateInheritedBaseTypes(this IEnumerable<TypeDescription>
}
}

/// <summary>
/// Populates inherited members (fields, properties, methods, constructors, enum members, and events) from base types into derived types.
/// This method adds non-private members from base types to derived types if they are not already present.
/// </summary>
/// <param name="types">The collection of type descriptions to process.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="types"/> is <c>null</c>.</exception>
/// <remarks>
/// This is a simplified inheritance implementation that does not handle complex scenarios like method overrides or hiding.
/// </remarks>
public static void PopulateInheritedMembers(this IEnumerable<TypeDescription> types)
{
ArgumentNullException.ThrowIfNull(types);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
namespace DendroDocs.Extensions;

/// <summary>
/// Provides extension methods for working with method invocation descriptions and matching them against method definitions.
/// </summary>
public static class InvocationDescriptionExtensions
{
/// <summary>
/// Determines whether the specified invocation matches the given method definition.
/// </summary>
/// <param name="invocation">The method invocation to check.</param>
/// <param name="method">The method definition to match against.</param>
/// <returns><c>true</c> if the invocation matches the method name and parameters; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="invocation"/> or <paramref name="method"/> is <c>null</c>.</exception>
public static bool MatchesMethod(this InvocationDescription invocation, IHaveAMethodBody method)
{
ArgumentNullException.ThrowIfNull(invocation);
Expand All @@ -10,6 +20,13 @@ public static bool MatchesMethod(this InvocationDescription invocation, IHaveAMe
return string.Equals(invocation.Name, method.Name) && invocation.MatchesParameters(method);
}

/// <summary>
/// Determines whether the parameters of the specified invocation match the given method definition.
/// </summary>
/// <param name="invocation">The method invocation to check.</param>
/// <param name="method">The method definition to match against.</param>
/// <returns><c>true</c> if the invocation parameters match the method parameters, considering optional parameters; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="invocation"/> or <paramref name="method"/> is <c>null</c>.</exception>
public static bool MatchesParameters(this InvocationDescription invocation, IHaveAMethodBody method)
{
ArgumentNullException.ThrowIfNull(invocation);
Expand Down
50 changes: 50 additions & 0 deletions src/DendroDocs.Client/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
namespace DendroDocs.Extensions;

/// <summary>
/// Provides extension methods for string operations related to type analysis and formatting.
/// </summary>
public static class StringExtensions
{
/// <summary>
/// Determines whether the specified type name represents an enumerable collection type.
/// </summary>
/// <param name="type">The full type name to check.</param>
/// <returns><c>true</c> if the type is an enumerable collection; otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type"/> is <c>null</c>.</exception>
public static bool IsEnumerable(this string type)
{
ArgumentNullException.ThrowIfNull(type);
Expand All @@ -22,13 +31,31 @@ public static bool IsEnumerable(this string type)
return !type.Contains("Enumerator") && !type.Contains("Compar") && !type.Contains("Structural") && !type.Contains("Provider");
}

/// <summary>
/// Determines whether the specified type name represents a generic type.
/// </summary>
/// <param name="type">The type name to check for generic type indicators.</param>
/// <returns><c>true</c> if the type is generic (contains angle brackets); otherwise, <c>false</c>.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type"/> is <c>null</c>.</exception>
public static bool IsGeneric(this string type)
{
ArgumentNullException.ThrowIfNull(type);

return type.IndexOf('>') > -1 && type.TrimEnd().EndsWith('>');
}

/// <summary>
/// Extracts the generic type arguments from a generic type name.
/// </summary>
/// <param name="type">The generic type name to parse.</param>
/// <returns>A read-only list containing the generic type arguments. Returns an empty list if the type is not generic.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type"/> is <c>null</c>.</exception>
/// <example>
/// <code>
/// var genericTypes = "System.Collections.Generic.List&lt;System.String&gt;".GenericTypes();
/// // Returns: ["System.String"]
/// </code>
/// </example>
public static IReadOnlyList<string> GenericTypes(this string type)
{
ArgumentNullException.ThrowIfNull(type);
Expand Down Expand Up @@ -58,6 +85,18 @@ public static IReadOnlyList<string> GenericTypes(this string type)
return types;
}

/// <summary>
/// Formats a type name for display in diagrams by removing namespace qualifiers and preserving generic type structure.
/// </summary>
/// <param name="type">The full type name to format.</param>
/// <returns>A simplified type name suitable for diagram display.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type"/> is <c>null</c>.</exception>
/// <example>
/// <code>
/// var diagramName = "System.Collections.Generic.List&lt;System.String&gt;".ForDiagram();
/// // Returns: "List&lt;String&gt;"
/// </code>
/// </example>
public static string ForDiagram(this string type)
{
ArgumentNullException.ThrowIfNull(type);
Expand All @@ -78,6 +117,17 @@ public static string ForDiagram(this string type)
}
}

/// <summary>
/// Converts a string to sentence case by adding spaces before uppercase letters and digits.
/// </summary>
/// <param name="type">The string to convert to sentence case.</param>
/// <returns>The string converted to sentence case with appropriate spacing.</returns>
/// <example>
/// <code>
/// var sentence = "MyPropertyName".ToSentenceCase();
/// // Returns: "My Property Name"
/// </code>
/// </example>
public static string ToSentenceCase(this string type)
{
if (string.IsNullOrEmpty(type))
Expand Down