diff --git a/src/ClassGraph/ClassGraph.cs b/src/ClassGraph/ClassGraph.cs index 3badb06..9e30a1e 100644 --- a/src/ClassGraph/ClassGraph.cs +++ b/src/ClassGraph/ClassGraph.cs @@ -1,4 +1,4 @@ -namespace DiagramGenerator.ClassGraph; +namespace ClassGraph; public class Graph { diff --git a/src/ClassGraph/DiagramDirection.cs b/src/ClassGraph/DiagramDirection.cs new file mode 100644 index 0000000..3b46fd7 --- /dev/null +++ b/src/ClassGraph/DiagramDirection.cs @@ -0,0 +1,9 @@ +namespace ClassGraph; + +public enum DiagramDirection +{ + TB, // Top to Bottom + BT, // Bottom to Top + LR, // Left to Right + RL // Right to Left +} \ No newline at end of file diff --git a/src/ClassGraph/IDiagramGenerator.cs b/src/ClassGraph/IDiagramGenerator.cs index 103995c..deec951 100644 --- a/src/ClassGraph/IDiagramGenerator.cs +++ b/src/ClassGraph/IDiagramGenerator.cs @@ -1,6 +1,6 @@ -namespace DiagramGenerator.ClassGraph; +namespace ClassGraph; public interface IDiagramGenerator { - string Generate(Graph graph); + string Generate(Graph graph, bool highLevelOnly, DiagramDirection diagramDirection); } \ No newline at end of file diff --git a/src/ClassGraph/IGraphBuilder.cs b/src/ClassGraph/IGraphBuilder.cs index 561fd9c..a339c85 100644 --- a/src/ClassGraph/IGraphBuilder.cs +++ b/src/ClassGraph/IGraphBuilder.cs @@ -1,4 +1,4 @@ -namespace DiagramGenerator.ClassGraph; +namespace ClassGraph; public interface IGraphBuilder { diff --git a/src/ClassGraph/MermaidGenerator.cs b/src/ClassGraph/MermaidGenerator.cs index 593fa2f..de834ea 100644 --- a/src/ClassGraph/MermaidGenerator.cs +++ b/src/ClassGraph/MermaidGenerator.cs @@ -1,9 +1,10 @@ -namespace DiagramGenerator.ClassGraph; +namespace ClassGraph; public class MermaidGenerator : IDiagramGenerator { private static string MDFrame = @"```mermaid classDiagram + direction {2} {0} {1} @@ -15,16 +16,23 @@ public class MermaidGenerator : IDiagramGenerator { {1}{2} }}"; - public string Generate(Graph graph) { + public string Generate(Graph graph, bool highLevelOnly, DiagramDirection diagramDirection) { var allClass = new List(); foreach (var @class in graph.Classes) { - var classString = GenerateClass(@class); + // fixed an issue where a class with empty name would generate invalid + // Mermaid syntax and break the entire diagram - just skip any classes with empty/whitespace names + if (string.IsNullOrWhiteSpace(@class.Name)) continue; + var classString = GenerateClass(@class, highLevelOnly); allClass.Add(classString); } var allRelation = new List(); foreach (var relation in graph.Relations) { - var relationString = GenerateRelation(relation); + // if either end of the relation has empty/whitespace name, skip it to avoid breaking Mermaid syntax + if (string.IsNullOrWhiteSpace(relation.From?.Name) || + string.IsNullOrWhiteSpace(relation.To?.Name)) continue; + + var relationString = GenerateRelation(relation); allRelation.Add(relationString); } @@ -40,10 +48,10 @@ public string Generate(Graph graph) { sections += "\r\n\r\n" + relationSection; } - return string.Format(MDFrame, sections, string.Empty); + return string.Format(MDFrame, sections, string.Empty, diagramDirection.ToString()); } - private string GenerateClass(Class @class) { + private string GenerateClass(Class @class, bool highLevelOnly) { var lines = new List(); // Add type annotation (<>, <>, etc.) as first line inside class block @@ -52,6 +60,10 @@ private string GenerateClass(Class @class) { lines.Add($" {typeAnnotation}"); } + if (highLevelOnly) { + return string.Format(ClassFrame, @class.Name, string.Empty, string.Empty); + } + // For enums, add enum values instead of properties/methods if (@class.Kind == TypeKind.Enum) { foreach (var enumValue in @class.EnumValues) { diff --git a/src/ClassGraph/SourceGraphBuilder.cs b/src/ClassGraph/SourceGraphBuilder.cs index a8f05ab..6403164 100644 --- a/src/ClassGraph/SourceGraphBuilder.cs +++ b/src/ClassGraph/SourceGraphBuilder.cs @@ -2,7 +2,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace DiagramGenerator.ClassGraph; +namespace ClassGraph; public class SourceGraphBuilder : IGraphBuilder { diff --git a/src/MermaidClassDiagramGenerator/Program.cs b/src/MermaidClassDiagramGenerator/Program.cs index 3de68ce..87200b2 100644 --- a/src/MermaidClassDiagramGenerator/Program.cs +++ b/src/MermaidClassDiagramGenerator/Program.cs @@ -1,5 +1,5 @@ using System.CommandLine; -using DiagramGenerator.ClassGraph; +using ClassGraph; var outputOption = new Option( aliases: new[] { "--output", "-o" }, @@ -20,7 +20,8 @@ var tnOption = new Option>( aliases: new[] { "--type-names", "-t" }, description: "Specific classes to include.", - getDefaultValue: () => new List()); + getDefaultValue: () => new List()) + { AllowMultipleArgumentsPerToken = true }; var ignoreDependencyOption = new Option( name: "--ignore-dependency", @@ -44,6 +45,16 @@ description: "Additional patterns to exclude from file search (e.g., 'Migrations', 'Generated').", getDefaultValue: () => new List()); +var highLevelOnlyOption = new Option( + aliases: new[] { "--high-level-only" }, + description: "Exclude all properties, methods, etc from generated diagram.", + getDefaultValue: () => false); + +var diagramDirectionOption = new Option( + aliases: new[] { "--diagram-direction", "-d" }, + description: "The direction that diagram should be generated (TB: Top to Bottom (default), BT: Bottom to Top, LR: Left to Right, RL: Right to Left)", + getDefaultValue: () => DiagramDirection.TB); + var rootCommand = new RootCommand("Generate mermaid.js class-diagram from C# source code files."); rootCommand.AddOption(outputOption); rootCommand.AddOption(nsOption); @@ -54,6 +65,8 @@ rootCommand.AddOption(visibilityOption); rootCommand.AddOption(verboseOption); rootCommand.AddOption(excludePatternsOption); +rootCommand.AddOption(highLevelOnlyOption); +rootCommand.AddOption(diagramDirectionOption); rootCommand.SetHandler((context) => { @@ -66,8 +79,10 @@ var visLevel = context.ParseResult.GetValueForOption(visibilityOption); var verbose = context.ParseResult.GetValueForOption(verboseOption); var excludePatterns = context.ParseResult.GetValueForOption(excludePatternsOption); + var highLevelOnly = context.ParseResult.GetValueForOption(highLevelOnlyOption); + var diagramDirection = context.ParseResult.GetValueForOption(diagramDirectionOption); - Execute(output!, ns!, inputPath!, tns!, ignoreDep, excludeSys, visLevel!, verbose, excludePatterns!); + Execute(output!, ns!, inputPath!, tns!, ignoreDep, excludeSys, visLevel!, verbose, excludePatterns!, highLevelOnly, diagramDirection); }); return await rootCommand.InvokeAsync(args); @@ -80,7 +95,9 @@ static void Execute(FileInfo outputFile, bool excludeSystemTypes, string visibilityLevel, bool verbose, - IList excludePatterns) + IList excludePatterns, + bool highLevelOnly, + DiagramDirection diagramDirection) { try { @@ -142,7 +159,7 @@ static void Execute(FileInfo outputFile, // 3. Generate Mermaid diagram var generator = new MermaidGenerator(); - var text = generator.Generate(graph); + var text = generator.Generate(graph, highLevelOnly, diagramDirection); // 4. Write output File.WriteAllText(outputFile.FullName, text); diff --git a/src/MermaidClassDiagramGenerator/Properties/launchSettings.json b/src/MermaidClassDiagramGenerator/Properties/launchSettings.json new file mode 100644 index 0000000..018b357 --- /dev/null +++ b/src/MermaidClassDiagramGenerator/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "MermaidClassDiagramGenerator": { + "commandName": "Project" + } + } +} \ No newline at end of file