2972e59e00
* Fixed dependency * Updates to NeoDictionary * Changes to NeoDictionary * Started working on a Git implementation to integrate into Game Engines in the future * Added commit functionality to the git library * New changes * Added select for NeoDictionary.cs * Fixed issue with previous new method * Added a Benchmarking Tool to be used mostly in game engines * Added IndexOf to NeoDictionary.cs * Added new method for BenchmarkUtility.cs * added an instacedobject class * Added a new shuffle method for lists * fixed return type * Added new Changelog generator * Added new Game logic library to assist with game projects * Added Web Project * Added Nuget package configs * Add workflow for publishing game library NuGet package * Add README for OmegaLeo's Helper Library * Updated Website to reflect all libraries * Fixed missing library in Changelog * Added Contributing and Code of Conduct * Fixed web project * new documentation changes * fix packages * Fixed DocumentationHelper package dependencies * Added Serilog * Added logging * Fixed documentation * Added logging for cache * fixed documentation * Updated README.md * Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update OmegaLeo.HelperLib.Web/Controllers/HomeController.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update OmegaLeo.HelperLib.Web/Controllers/HomeController.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix error * changes for Github Packages * Cleanup * Started working on a SteamWorks integration * Added logging * Started working on a Steam library --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
282 lines
11 KiB
C#
Executable File
282 lines
11 KiB
C#
Executable File
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Xml;
|
|
using Microsoft.Extensions.DependencyModel;
|
|
using OmegaLeo.HelperLib.Shared.Attributes;
|
|
using OmegaLeo.HelperLib.Documentation.Models;
|
|
|
|
namespace OmegaLeo.HelperLib.Documentation.Helpers
|
|
{
|
|
|
|
[Changelog("1.2.0", "Migrated DocumentationHelper library from NetFlow.DocumentationHelper.Library to it's own package in OmegaLeo.HelperLib", "January 26, 2026")]
|
|
[Changelog("1.3.0", "GenerateDocumentation now includes method parameters and return type in the generated documentation", "April 14, 2026")]
|
|
public static class DocumentationHelperTool
|
|
{
|
|
[Documentation("GenerateDocumentation (bool generateForPackageAssembly)",
|
|
@"Generates a List of objects of type DocumentationStructure that contain the following fields:
|
|
**AssemblyName**: Name of the main Assembly, used to identify the root namespace
|
|
**ClassName**: Name of the class, used to identify the upper level object
|
|
**Title**: Title what we're generating documentation for
|
|
**Description**: Description of what we're generating documentation for, this can contain usage examples and can use the args array to pass names(e.g.: This method uses this methodology)
|
|
**Args**: Array of strings that describe the parameters of the method or class
|
|
**MethodParameters**: Reflected parameter signature list for documented methods (e.g. `value: int`, `name: string`)
|
|
**ReturnType**: Reflected return type for documented methods (e.g. `bool`, `List<string>`)
|
|
**CodeExample**: A code example of how to use the method or class
|
|
|
|
*Note: If generateForPackageAssembly is set to true, this will generate documentation for the library as well.*",
|
|
new []
|
|
{
|
|
"generateForPackageAssembly - Generate documentation for the DocumentationHelper library as well?"
|
|
},
|
|
@"```csharp
|
|
DocumentationHelperTool.GenerateDocumentation(true); // Generates documentation for all assemblies including the DocumentationHelper.Library package assembly
|
|
DocumentationHelperTool.GenerateDocumentation(false); // Generates documentation for all assemblies excluding the DocumentationHelper.Library package assembly
|
|
```")]
|
|
public static IEnumerable<DocumentationStructure> GenerateDocumentation(bool generateForPackageAssembly = false)
|
|
{
|
|
EnsureReferencedAssembliesLoaded();
|
|
|
|
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
|
|
.Where(a => !a.IsDynamic)
|
|
.Select(a =>
|
|
new DocumentationAssembly()
|
|
{
|
|
AssemblyName = a.GetName(false)?.Name ?? nameof(a),
|
|
Types = GetTypesWithDocumentationAttribute(a)
|
|
}).Where(x => x.Types.Any()).ToList();
|
|
|
|
if (!generateForPackageAssembly)
|
|
{
|
|
assemblies =
|
|
assemblies.Where(x => x.AssemblyName != typeof(DocumentationHelperTool).Assembly.GetName().Name)
|
|
.ToList();
|
|
}
|
|
|
|
foreach (var assembly in assemblies)
|
|
{
|
|
foreach (var type in assembly.Types)
|
|
{
|
|
var doc = GetDocumentation(type, assembly.AssemblyName);
|
|
yield return doc;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void EnsureReferencedAssembliesLoaded()
|
|
{
|
|
try
|
|
{
|
|
var loadedNames = AppDomain.CurrentDomain.GetAssemblies()
|
|
.Where(a => a.GetName(false) != null)
|
|
.Select(a => a.GetName(false).Name)
|
|
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
|
|
|
var deps = DependencyContext.Default?.RuntimeLibraries;
|
|
if (deps == null) return;
|
|
|
|
foreach (var lib in deps)
|
|
{
|
|
if (!loadedNames.Contains(lib.Name))
|
|
{
|
|
try
|
|
{
|
|
Assembly.Load(new AssemblyName(lib.Name));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.Error.WriteLine($"warning OMEGA001: Failed to load assembly '{lib.Name}': {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// best-effort only
|
|
Console.Error.WriteLine($"warning OMEGA001: EnsureReferencedAssembliesLoaded failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
static IEnumerable<Type> GetTypesWithDocumentationAttribute(Assembly assembly)
|
|
{
|
|
Type[] types;
|
|
try
|
|
{
|
|
types = assembly.GetTypes();
|
|
}
|
|
catch (ReflectionTypeLoadException ex)
|
|
{
|
|
types = ex.Types.Where(t => t != null).ToArray();
|
|
}
|
|
|
|
foreach (Type type in types)
|
|
{
|
|
if (type == null) continue;
|
|
|
|
var classHasDoc = type.GetCustomAttributes(typeof(DocumentationAttribute), true).Any();
|
|
var fieldsHaveDoc = type.GetFields()
|
|
.Any(f => f.GetCustomAttributes(typeof(DocumentationAttribute), true).Any());
|
|
var typesHaveDoc = type.GetProperties()
|
|
.Any(p => p.GetCustomAttributes(typeof(DocumentationAttribute), true).Any());
|
|
var methodsHaveDoc = type.GetMethods()
|
|
.Any(m => m.GetCustomAttributes(typeof(DocumentationAttribute), true).Any());
|
|
|
|
if (classHasDoc || fieldsHaveDoc || typesHaveDoc || methodsHaveDoc)
|
|
{
|
|
yield return type;
|
|
}
|
|
}
|
|
}
|
|
|
|
[Documentation(nameof(GetDocumentation), "Generates a DocumentationStructure object for the given Type and Assembly Name",
|
|
new []
|
|
{
|
|
"type - The Type to generate documentation for",
|
|
"assemblyName - The name of the assembly the Type belongs to"
|
|
},
|
|
@"```csharp
|
|
var doc = GetDocumentation(typeof(MyClass), ""MyAssembly"");
|
|
```")]
|
|
static DocumentationStructure GetDocumentation(Type type, string assemblyName)
|
|
{
|
|
var classDocs = type.GetCustomAttributes(typeof(DocumentationAttribute), true)
|
|
.Select(x => (DocumentationAttribute)x);
|
|
|
|
var fieldDocs = type.GetFields().SelectMany(f =>
|
|
f.GetCustomAttributes(typeof(DocumentationAttribute), true).Select(x => (DocumentationAttribute)x));
|
|
var propertyDocs = type.GetProperties().SelectMany(p =>
|
|
p.GetCustomAttributes(typeof(DocumentationAttribute), true).Select(x => (DocumentationAttribute)x));
|
|
var methodDocs = type.GetMethods().SelectMany(m =>
|
|
m.GetCustomAttributes(typeof(DocumentationAttribute), true)
|
|
.Select(x => new
|
|
{
|
|
Method = m,
|
|
Attribute = (DocumentationAttribute)x
|
|
}));
|
|
|
|
var docStructure = new DocumentationStructure(assemblyName, type.Name);
|
|
|
|
foreach (var doc in classDocs)
|
|
{
|
|
docStructure.AddDescription(new DocumentationDescription(doc.Title, doc.Description, doc.Args, doc.CodeExample));
|
|
}
|
|
|
|
foreach (var doc in fieldDocs)
|
|
{
|
|
docStructure.AddDescription(new DocumentationDescription(doc.Title, doc.Description, doc.Args, doc.CodeExample));
|
|
}
|
|
|
|
foreach (var doc in propertyDocs)
|
|
{
|
|
docStructure.AddDescription(new DocumentationDescription(doc.Title, doc.Description, doc.Args, doc.CodeExample));
|
|
}
|
|
|
|
foreach (var methodDoc in methodDocs)
|
|
{
|
|
var reflectedParameters = GetMethodParameters(methodDoc.Method);
|
|
var returnType = GetFriendlyTypeName(methodDoc.Method.ReturnType);
|
|
|
|
var mergedParameters = (methodDoc.Attribute.Args ?? Array.Empty<string>())
|
|
.Concat(reflectedParameters)
|
|
.ToArray();
|
|
|
|
docStructure.AddDescription(new DocumentationDescription(
|
|
methodDoc.Attribute.Title,
|
|
methodDoc.Attribute.Description,
|
|
mergedParameters,
|
|
methodDoc.Attribute.CodeExample,
|
|
reflectedParameters,
|
|
returnType));
|
|
}
|
|
|
|
return docStructure;
|
|
}
|
|
|
|
static string[] GetMethodParameters(MethodInfo method)
|
|
{
|
|
return method.GetParameters()
|
|
.Select(parameter =>
|
|
{
|
|
var prefix = parameter.IsOut
|
|
? "out "
|
|
: parameter.ParameterType.IsByRef
|
|
? "ref "
|
|
: string.Empty;
|
|
|
|
var parameterType = parameter.ParameterType.IsByRef
|
|
? parameter.ParameterType.GetElementType() ?? parameter.ParameterType
|
|
: parameter.ParameterType;
|
|
|
|
return $"{parameter.Name}: {prefix}{GetFriendlyTypeName(parameterType)}";
|
|
})
|
|
.ToArray();
|
|
}
|
|
|
|
static string GetFriendlyTypeName(Type type)
|
|
{
|
|
if (type == typeof(void)) return "void";
|
|
if (type == typeof(bool)) return "bool";
|
|
if (type == typeof(byte)) return "byte";
|
|
if (type == typeof(sbyte)) return "sbyte";
|
|
if (type == typeof(char)) return "char";
|
|
if (type == typeof(decimal)) return "decimal";
|
|
if (type == typeof(double)) return "double";
|
|
if (type == typeof(float)) return "float";
|
|
if (type == typeof(int)) return "int";
|
|
if (type == typeof(uint)) return "uint";
|
|
if (type == typeof(long)) return "long";
|
|
if (type == typeof(ulong)) return "ulong";
|
|
if (type == typeof(short)) return "short";
|
|
if (type == typeof(ushort)) return "ushort";
|
|
if (type == typeof(string)) return "string";
|
|
if (type == typeof(object)) return "object";
|
|
|
|
if (type.IsArray)
|
|
{
|
|
return $"{GetFriendlyTypeName(type.GetElementType() ?? typeof(object))}[]";
|
|
}
|
|
|
|
if (type.IsGenericType)
|
|
{
|
|
var name = type.Name;
|
|
var tickIndex = name.IndexOf('`');
|
|
if (tickIndex > 0)
|
|
{
|
|
name = name.Substring(0, tickIndex);
|
|
}
|
|
|
|
var genericArguments = type.GetGenericArguments()
|
|
.Select(GetFriendlyTypeName);
|
|
|
|
return $"{name}<{string.Join(", ", genericArguments)}>";
|
|
}
|
|
|
|
var nullable = Nullable.GetUnderlyingType(type);
|
|
if (nullable != null)
|
|
{
|
|
return $"{GetFriendlyTypeName(nullable)}?";
|
|
}
|
|
|
|
return type.Name;
|
|
}
|
|
|
|
static Dictionary<string, string> loadedXmlDocumentation =
|
|
new Dictionary<string, string>();
|
|
public static void LoadXmlDocumentation(string xmlDocumentation)
|
|
{
|
|
using (XmlReader xmlReader = XmlReader.Create(new StringReader(xmlDocumentation)))
|
|
{
|
|
while (xmlReader.Read())
|
|
{
|
|
if (xmlReader.NodeType == XmlNodeType.Element && xmlReader.Name == "member")
|
|
{
|
|
string raw_name = xmlReader["name"];
|
|
loadedXmlDocumentation[raw_name] = xmlReader.ReadInnerXml();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |