Skip to content

Commit c6a1ab3

Browse files
feat: Enhance documentation generation with new output paths and support for Markdown/AsciiDoc formats
1 parent bc8bf0f commit c6a1ab3

File tree

9 files changed

+145
-35
lines changed

9 files changed

+145
-35
lines changed

DotAutoDocConfig.Core/ComponentModel/Attributes/DocumentationAttribute.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
namespace DotAutoDocConfig.Core.ComponentModel.Attributes;
44

5-
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
6-
public class DocumentationAttribute(DocumentationFormat format) : Attribute
5+
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
6+
public class DocumentationAttribute(DocumentationFormat format, string outputPath) : Attribute
77
{
88
public DocumentationFormat Format { get; } = format;
9+
10+
public string OutputPath { get; set; } = outputPath;
911
}
Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
using System;
2-
31
namespace DotAutoDocConfig.Core.ComponentModel;
42

5-
[Flags]
63
public enum DocumentationFormat : byte
74
{
85
None = 0,
9-
AsciiDoc = 1 << 0,
10-
Markdown = 1 << 1,
11-
Html = 1 << 2
6+
AsciiDoc = 1,
7+
Markdown = 2,
8+
Html = 3
129
}

DotAutoDocConfig.Sample.Console/AppConfiguration.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ namespace DotAutoDocConfig.Sample.Console;
66
/// <summary>
77
///
88
/// </summary>
9-
[Documentation(DocumentationFormat.Markdown | DocumentationFormat.AsciiDoc)]
9+
[Documentation(DocumentationFormat.Markdown, "docs/AppConfiguration.md")]
10+
[Documentation(DocumentationFormat.AsciiDoc, "docs/AppConfiguration.adoc")]
1011
public class AppConfiguration
1112
{
1213
public int MaxItems { get; set; } = 100;

DotAutoDocConfig.SourceGenerator.Tests/SourceGeneratorWithAttributesTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public IEnumerable<string> Report()
4040
public async Task GenerateReportMethod()
4141
{
4242
// Create an instance of the source generator.
43-
SourceGeneratorWithAttributes generator = new();
43+
DocumentationSourceGenerator generator = new();
4444

4545
// Source generators should be tested using 'GeneratorDriver'.
4646
CSharpGeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Linq;
2+
using System.Text;
3+
using Microsoft.CodeAnalysis;
4+
5+
namespace DotAutoDocConfig.SourceGenerator.DocumentationGenerators;
6+
7+
public static class AsciiDocGenerator
8+
{
9+
public static void GenerateAsciiDoc(StringBuilder sb, INamedTypeSymbol classSymbol)
10+
{
11+
sb.AppendLine("= Configuration Documentation");
12+
sb.AppendLine();
13+
sb.AppendLine($"== {classSymbol.Name}");
14+
sb.AppendLine();
15+
16+
foreach (var member in classSymbol.GetMembers().OfType<IPropertySymbol>())
17+
{
18+
// Check for ExcludeFromDocumentation attribute
19+
var excludeAttribute = member.GetAttributes()
20+
.FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() ==
21+
"DotAutoDocConfig.Core.ComponentModel.Attributes.ExcludeFromDocumentationAttribute");
22+
23+
if (excludeAttribute != null)
24+
{
25+
continue; // Skip this property
26+
}
27+
28+
sb.AppendLine($"=== {member.Name}");
29+
sb.AppendLine();
30+
sb.AppendLine($"Type: `{member.Type.ToDisplayString()}`");
31+
sb.AppendLine();
32+
sb.AppendLine($"Default Value: `{GetDefaultValue(member)}`");
33+
sb.AppendLine();
34+
}
35+
}
36+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Linq;
2+
using System.Text;
3+
using Microsoft.CodeAnalysis;
4+
5+
namespace DotAutoDocConfig.SourceGenerator.DocumentationGenerators;
6+
7+
public static class MarkdownGenerator
8+
{
9+
public static void Generate(StringBuilder sb, INamedTypeSymbol classSymbol)
10+
{
11+
sb.AppendLine("= Configuration Documentation");
12+
sb.AppendLine();
13+
sb.AppendLine($"== {classSymbol.Name}");
14+
sb.AppendLine();
15+
16+
foreach (var member in classSymbol.GetMembers().OfType<IPropertySymbol>())
17+
{
18+
// Check for ExcludeFromDocumentation attribute
19+
var excludeAttribute = member.GetAttributes()
20+
.FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() ==
21+
"DotAutoDocConfig.Core.ComponentModel.Attributes.ExcludeFromDocumentationAttribute");
22+
23+
if (excludeAttribute != null)
24+
{
25+
continue; // Skip this property
26+
}
27+
28+
sb.AppendLine($"=== {member.Name}");
29+
sb.AppendLine();
30+
sb.AppendLine($"Type: `{member.Type.ToDisplayString()}`");
31+
sb.AppendLine();
32+
sb.AppendLine($"Default Value: `{GetDefaultValue(member)}`");
33+
sb.AppendLine();
34+
}
35+
}
36+
}

DotAutoDocConfig.SourceGenerator/SourceGeneratorWithAttributes.cs renamed to DotAutoDocConfig.SourceGenerator/DocumentationSourceGenerator.cs

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
using System.Collections.Immutable;
33
using System.Linq;
44
using System.Text;
5+
using DotAutoDocConfig.Core.ComponentModel;
6+
using DotAutoDocConfig.Core.ComponentModel.Attributes;
7+
using DotAutoDocConfig.SourceGenerator.Models;
58
using Microsoft.CodeAnalysis;
69
using Microsoft.CodeAnalysis.CSharp.Syntax;
710
using Microsoft.CodeAnalysis.Text;
@@ -13,42 +16,54 @@ namespace DotAutoDocConfig.SourceGenerator;
1316
/// A sample source generator that creates a custom report based on class properties. The target class should be annotated with the 'Generators.ReportAttribute' attribute.
1417
/// When using the source code as a baseline, an incremental source generator is preferable because it reduces the performance overhead.
1518
/// </summary>
16-
[Generator]
17-
public class SourceGeneratorWithAttributes : IIncrementalGenerator
19+
[Generator(LanguageNames.CSharp)]
20+
public class DocumentationSourceGenerator : IIncrementalGenerator
1821
{
19-
private const string Namespace = "Generators";
20-
private const string AttributeName = "ReportAttribute";
21-
22-
private const string AttributeSourceCode = $@"// <auto-generated/>
23-
24-
namespace {Namespace}
25-
{{
26-
[System.AttributeUsage(System.AttributeTargets.Class)]
27-
public class {AttributeName} : System.Attribute
28-
{{
29-
}}
30-
}}";
31-
3222
public void Initialize(IncrementalGeneratorInitializationContext context)
3323
{
34-
// Add the marker attribute to the compilation.
35-
context.RegisterPostInitializationOutput(ctx => ctx.AddSource(
36-
$"{AttributeName}.g.cs",
37-
SourceText.From(AttributeSourceCode, Encoding.UTF8)));
38-
3924
// Filter classes annotated with the [Report] attribute. Only filtered Syntax Nodes can trigger code generation.
4025
IncrementalValuesProvider<ClassDeclarationSyntax> provider = context.SyntaxProvider
41-
.CreateSyntaxProvider(
42-
(s, _) => s is ClassDeclarationSyntax,
43-
(ctx, _) => GetClassDeclarationForSourceGen(ctx))
44-
.Where(t => t.reportAttributeFound)
45-
.Select((t, _) => t.Item1);
26+
.ForAttributeWithMetadataName(
27+
$"{typeof(DocumentationAttribute).Namespace}.{nameof(DocumentationAttribute)}",
28+
static (node, _) => node is ClassDeclarationSyntax,
29+
static (ctx, _) => (ClassDeclarationSyntax)ctx.TargetNode);
4630

4731
// Generate the source code.
4832
context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()),
4933
((ctx, t) => GenerateCode(ctx, t.Left, t.Right)));
5034
}
5135

36+
private static (INamedTypeSymbol?, List<DocumentationOptionsDataModel>) GetDocumentationDataModels(
37+
Compilation compilation,
38+
ClassDeclarationSyntax classDeclarationSyntax)
39+
{
40+
List<DocumentationOptionsDataModel> documentationDataModels = [];
41+
42+
// We need to get semantic model of the class to retrieve
43+
SemanticModel semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree);
44+
if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol)
45+
return (null, documentationDataModels);
46+
// Go through all attributes of the class.
47+
foreach (AttributeData attributeData in classSymbol.GetAttributes())
48+
{
49+
string attributeName = attributeData.AttributeClass?.ToDisplayString() ?? string.Empty;
50+
// Check the full name of the [Documentation] attribute.
51+
if (attributeName != $"{typeof(DocumentationAttribute).Namespace}.{nameof(DocumentationAttribute)}")
52+
continue;
53+
// Retrieve constructor arguments.
54+
if (attributeData.ConstructorArguments.Length != 2)
55+
continue;
56+
DocumentationFormat format = (DocumentationFormat)attributeData.ConstructorArguments[0].Value!;
57+
string outputPath = attributeData.ConstructorArguments[1].Value!.ToString() ?? string.Empty;
58+
documentationDataModels.Add(new DocumentationOptionsDataModel
59+
{
60+
Format = format,
61+
OutputPath = outputPath
62+
});
63+
}
64+
return (classSymbol, documentationDataModels);
65+
}
66+
5267
/// <summary>
5368
/// Checks whether the Node is annotated with the [Report] attribute and maps syntax context to the specific node type (ClassDeclarationSyntax).
5469
/// </summary>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace DotAutoDocConfig.SourceGenerator.Models;
4+
5+
public class DocumentationDataModel
6+
{
7+
public INamedTypeSymbol ClassSymbol { get; set; } = null!;
8+
public string ParameterName { get; set; } = null!;
9+
public string ParameterType { get; set; } = null!;
10+
public string DefaultValue { get; set; } = null!;
11+
public string Summary { get; set; } = null!;
12+
public string ExampleValue { get; set; } = null!;
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using DotAutoDocConfig.Core.ComponentModel;
2+
3+
namespace DotAutoDocConfig.SourceGenerator.Models;
4+
5+
public class DocumentationOptionsDataModel
6+
{
7+
public DocumentationFormat Format { get; set; }
8+
9+
public string OutputPath { get; set; } = null!;
10+
}

0 commit comments

Comments
 (0)