Skip to content

Commit

Permalink
#86 AI Context friendly documentation - refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Feb 27, 2025
1 parent 826b278 commit 9e0bc7e
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 4,493 deletions.
3 changes: 3 additions & 0 deletions build/Core/AIContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Build.Core;

record AIContext(IReadOnlyCollection<AIContextFile> Files);
3 changes: 3 additions & 0 deletions build/Core/AIContextFile.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Build.Core;

record AIContextFile(string FileName, AIContextSize Size, long SizeBytes);
6 changes: 6 additions & 0 deletions build/Core/AIContextSize.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Build.Core;

enum AIContextSize
{
Large
}
12 changes: 0 additions & 12 deletions build/Core/Doc/IMarkdown.cs

This file was deleted.

8 changes: 0 additions & 8 deletions build/Core/Doc/IXDocumentTools.cs

This file was deleted.

1 change: 0 additions & 1 deletion build/Core/Doc/Markdown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ namespace Build.Core.Doc;
class Markdown(
IDocumentWalker<MarkdownWriterContext> dotNetXmlDocumentWalker,
IDocumentVisitor<MarkdownWriterContext> markdownWriterVisitor)
: IMarkdown
{
public Task ConvertAsync(
XDocument document,
Expand Down
2 changes: 1 addition & 1 deletion build/Core/Doc/XDocumentTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace Build.Core.Doc;
using System.Xml.Linq;

[ExcludeFromCodeCoverage]
class XDocumentTools : IXDocumentTools
class XDocumentTools
{
public Task<XDocument> LoadAsync(TextReader textReader, LoadOptions options, CancellationToken cancellationToken) =>
XDocument.LoadAsync(textReader, options, cancellationToken);
Expand Down
19 changes: 19 additions & 0 deletions build/Core/ReadmeTools.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace Build.Core;

class ReadmeTools
{
public IEnumerable<char> FormatTitle(string title)
{
var isFirst = true;
foreach (var ch in title)
{
if (!isFirst && char.IsUpper(ch))
{
yield return ' ';
}

yield return ch;
isFirst = false;
}
}
}
129 changes: 129 additions & 0 deletions build/Core/Targets/AIContextTarget.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// ReSharper disable InvertIf
// ReSharper disable ConvertIfStatementToSwitchStatement
// ReSharper disable ClassNeverInstantiated.Global

namespace Build.Core.Targets;

using System.Xml.Linq;
using Doc;
using Enum=Enum;

class AiContextTarget(
Commands commands,
Env env,
Versions versions,
Settings settings,
Markdown markdown,
XDocumentTools xDocumentTools,
ReadmeTools readmeTools,
[Tag(typeof(CreateExamplesTarget))] ITarget<IReadOnlyCollection<ExampleGroup>> createExamplesTarget)
: IInitializable, ITarget<AIContext>
{
private const string ReadmeDir = "readme";
private const string AiContextReadmeFileTemplate = "AI_CONTEXT_{0}.md";

public Task InitializeAsync(CancellationToken cancellationToken) => commands.RegisterAsync(
this, "Generate AI context", "ai");

public async Task<AIContext> RunAsync(CancellationToken cancellationToken)
{
var examples = await createExamplesTarget.RunAsync(cancellationToken);
var aiContextFileTasks = Enum.GetValues<AIContextSize>()
.Select(size => CreateAiContextFile(
string.Format(AiContextReadmeFileTemplate, size.ToString().ToUpperInvariant()),
examples,
size,
cancellationToken));

var files = await Task.WhenAll(aiContextFileTasks);
return new AIContext(files);
}

private async Task<AIContextFile> CreateAiContextFile(string fileName, IReadOnlyCollection<ExampleGroup> examples, AIContextSize size, CancellationToken cancellationToken)
{
await using var writer = File.CreateText(fileName);
{
await writer.WriteLineAsync("# Pure.DI source code generator usage scenarios.");
foreach (var (groupName, exampleItems) in examples)
{
await writer.WriteLineAsync();
var groupTitle = new string(readmeTools.FormatTitle(groupName).ToArray());
await writer.WriteLineAsync($"## {groupTitle} scenarios");
foreach (var vars in exampleItems)
{
var description = vars[CreateExamplesTarget.DescriptionKey];
var code = vars[CreateExamplesTarget.BodyKey];
await writer.WriteLineAsync();
await writer.WriteLineAsync($"### {description}");
await writer.WriteLineAsync();
var header = vars[CreateExamplesTarget.HeaderKey];
if (!string.IsNullOrWhiteSpace(header))
{
await writer.WriteLineAsync(header);
await writer.WriteLineAsync();
}

await writer.WriteLineAsync("```c#");
await writer.WriteLineAsync(code);
await writer.WriteLineAsync("```");
await writer.WriteLineAsync();
var references = vars[CreateExamplesTarget.ReferencesKey].Split(";", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var refs = references.Length > 0 ? "s" : "";
await writer.WriteLineAsync($"To run the above code, the following NuGet package{refs} must be added:");
await writer.WriteLineAsync(" - [Pure.DI](https://www.nuget.org/packages/Pure.DI)");
foreach (var reference in references)
{
await writer.WriteLineAsync($" - [{reference}](https://www.nuget.org/packages/{reference})");
}

await writer.WriteLineAsync();

var footer = vars[CreateExamplesTarget.FooterKey];
if (!string.IsNullOrWhiteSpace(footer))
{
await writer.WriteLineAsync(footer);
}
}
}

await writer.WriteLineAsync();
await writer.WriteLineAsync("# Examples of using Pure.DI source code generator in different types of .NET applications.");
await writer.WriteLineAsync();
var generatorPackageVersion = versions.GetNext(new NuGetRestoreSettings("Pure.DI"), Settings.VersionRange, 0).ToString();
var msPackageVersion = versions.GetNext(new NuGetRestoreSettings("Pure.DI.MS"), Settings.VersionRange, 0).ToString();
foreach (var readmeFile in Directory.EnumerateFiles(Path.Combine(ReadmeDir), "*.md"))
{
if (readmeFile.EndsWith("Template.md", StringComparison.InvariantCultureIgnoreCase))
{
if (readmeFile.EndsWith("PageTemplate.md"))
{
var content = await File.ReadAllTextAsync(readmeFile);
content = content
.Replace("$(version)", generatorPackageVersion)
.Replace("$(ms.version)", msPackageVersion)
.Replace("$(targetFrameworkVersion)", $"net{settings.BaseDotNetFrameworkVersion}");
await writer.WriteLineAsync(content);
}
}
}

await writer.WriteLineAsync();
await writer.WriteLineAsync("# Pure.DI API");
await writer.WriteLineAsync();
var sourceDirectory = env.GetPath(PathType.SourceDirectory);
var xmlDocFile = Path.Combine(sourceDirectory, "Pure.DI.Core", "bin", settings.Configuration, "netstandard2.0", "Pure.DI.xml");
using var xmlDocReader = File.OpenText(xmlDocFile);
var xmlDoc = await xDocumentTools.LoadAsync(xmlDocReader, LoadOptions.None, CancellationToken.None);
await markdown.ConvertAsync(
xmlDoc,
writer,
i => i.NamespaceName == "Pure.DI" && i.TypeName != "Generator",
CancellationToken.None);

await writer.FlushAsync(cancellationToken);
}

var sizeBytes = new FileInfo(fileName).Length;
return new AIContextFile(fileName, size, sizeBytes);
}
}
113 changes: 7 additions & 106 deletions build/Core/Targets/ReadmeTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ class ReadmeTarget(
Versions versions,
Settings settings,
RootCommand rootCommand,
IMarkdown markdown,
IXDocumentTools xDocumentTools,
ReadmeTools readmeTools,
[Tag(typeof(CreateExamplesTarget))] ITarget<IReadOnlyCollection<ExampleGroup>> createExamplesTarget,
[Tag(typeof(BenchmarksTarget))] ITarget<int> benchmarksTarget)
[Tag(typeof(BenchmarksTarget))] ITarget<int> benchmarksTarget,
[Tag(typeof(AiContextTarget))] ITarget<AIContext> aiContextTarget)
: IInitializable, ITarget<int>
{
private const string ReadmeDir = "readme";
Expand Down Expand Up @@ -48,7 +48,7 @@ public async Task<int> RunAsync(CancellationToken cancellationToken)
Directory.Delete(generatedFiles, true);
}

var examplesSet = await createExamplesTarget.RunAsync(cancellationToken);
var examples = await createExamplesTarget.RunAsync(cancellationToken);

await using var readmeWriter = File.CreateText(ReadmeFile);

Expand All @@ -60,7 +60,7 @@ public async Task<int> RunAsync(CancellationToken cancellationToken)

await readmeWriter.WriteLineAsync();

await GenerateExamplesAsync(examplesSet, readmeWriter, logsDirectory);
await GenerateExamplesAsync(examples, readmeWriter, logsDirectory);

await AddContentAsync(FooterTemplateFile, readmeWriter);

Expand All @@ -76,95 +76,11 @@ public async Task<int> RunAsync(CancellationToken cancellationToken)

await contributingWriter.FlushAsync(cancellationToken);

await using var aiContextReadmeWriter = File.CreateText(AiContextReadmeFile);

await AddAiContextAsync(examplesSet, aiContextReadmeWriter);

await aiContextReadmeWriter.FlushAsync(cancellationToken);
await aiContextTarget.RunAsync(cancellationToken);

return 0;
}

private async Task AddAiContextAsync(IReadOnlyCollection<ExampleGroup> examples, StreamWriter writer)
{
await writer.WriteLineAsync("# Pure.DI source code generator usage scenarios.");
foreach (var (groupName, exampleItems) in examples)
{
await writer.WriteLineAsync();
var groupTitle = new string(FormatTitle(groupName).ToArray());
await writer.WriteLineAsync($"## {groupTitle} scenarios");
foreach (var vars in exampleItems)
{
var description = vars[CreateExamplesTarget.DescriptionKey];
var code = vars[CreateExamplesTarget.BodyKey];
await writer.WriteLineAsync();
await writer.WriteLineAsync($"### {description}");
await writer.WriteLineAsync();
var header = vars[CreateExamplesTarget.HeaderKey];
if (!string.IsNullOrWhiteSpace(header))
{
await writer.WriteLineAsync(header);
await writer.WriteLineAsync();
}

await writer.WriteLineAsync("```c#");
await writer.WriteLineAsync(code);
await writer.WriteLineAsync("```");
await writer.WriteLineAsync();
var references = vars[CreateExamplesTarget.ReferencesKey].Split(";", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
var refs = references.Length > 0 ? "s" : "";
await writer.WriteLineAsync($"To run the above code, the following NuGet package{refs} must be added:");
await writer.WriteLineAsync(" - [Pure.DI](https://www.nuget.org/packages/Pure.DI)");
foreach (var reference in references)
{
await writer.WriteLineAsync($" - [{reference}](https://www.nuget.org/packages/{reference})");
}

await writer.WriteLineAsync();

var footer = vars[CreateExamplesTarget.FooterKey];
if (!string.IsNullOrWhiteSpace(footer))
{
await writer.WriteLineAsync(footer);
}
}
}

await writer.WriteLineAsync();
await writer.WriteLineAsync("# Examples of using Pure.DI source code generator in different types of .NET applications.");
await writer.WriteLineAsync();
var generatorPackageVersion = versions.GetNext(new NuGetRestoreSettings("Pure.DI"), Settings.VersionRange, 0).ToString();
var msPackageVersion = versions.GetNext(new NuGetRestoreSettings("Pure.DI.MS"), Settings.VersionRange, 0).ToString();
foreach (var readmeFile in Directory.EnumerateFiles(Path.Combine(ReadmeDir), "*.md"))
{
if (readmeFile.EndsWith("Template.md", StringComparison.InvariantCultureIgnoreCase))
{
if (readmeFile.EndsWith("PageTemplate.md"))
{
var content = await File.ReadAllTextAsync(readmeFile);
content = content
.Replace("$(version)", generatorPackageVersion)
.Replace("$(ms.version)", msPackageVersion)
.Replace("$(targetFrameworkVersion)", $"net{settings.BaseDotNetFrameworkVersion}");
await writer.WriteLineAsync(content);
}
}
}

await writer.WriteLineAsync();
await writer.WriteLineAsync("# Pure.DI API");
await writer.WriteLineAsync();
var sourceDirectory = env.GetPath(PathType.SourceDirectory);
var xmlDocFile = Path.Combine(sourceDirectory, "Pure.DI.Core", "bin", settings.Configuration, "netstandard2.0", "Pure.DI.xml");
using var xmlDocReader = File.OpenText(xmlDocFile);
var xmlDoc = await xDocumentTools.LoadAsync(xmlDocReader, LoadOptions.None, CancellationToken.None);
await markdown.ConvertAsync(
xmlDoc,
writer,
i => i.NamespaceName == "Pure.DI" && i.TypeName != "Generator",
CancellationToken.None);
}

private async Task AddContributingAsync(StreamWriter writer)
{
await AddContentAsync(ContributingTemplateFile, writer, onLine: async line => {
Expand Down Expand Up @@ -225,7 +141,7 @@ private async Task GenerateExamplesAsync(IReadOnlyCollection<ExampleGroup> examp

foreach (var (groupName, exampleItems) in examples)
{
var groupTitle = new string(FormatTitle(groupName).ToArray());
var groupTitle = new string(readmeTools.FormatTitle(groupName).ToArray());
Info($"Processing examples group \"{groupTitle}\"");
await writer.WriteLineAsync($"### {groupTitle}");
foreach (var vars in exampleItems)
Expand Down Expand Up @@ -456,21 +372,6 @@ private static async Task AddBenchmarksAsync(string logsDirectory, TextWriter re
}
}

private static IEnumerable<char> FormatTitle(string title)
{
var isFirst = true;
foreach (var ch in title)
{
if (!isFirst && char.IsUpper(ch))
{
yield return ' ';
}

yield return ch;
isFirst = false;
}
}

private static string CreateExampleFileName(string text) =>
text.Replace(" ", "-")
.Replace("_", string.Empty)
Expand Down
3 changes: 2 additions & 1 deletion build/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
.Bind(Tag.Type).To<InstallTemplateTarget>()
.Bind(Tag.Type).To<UpdateTarget>()
.Bind(Tag.Type).To<PublishBlazorTarget>()
.Bind(Tag.Type).To<PerformanceTestsTarget>();
.Bind(Tag.Type).To<PerformanceTestsTarget>()
.Bind(Tag.Type).To<AiContextTarget>();

return await new Composition().Root.RunAsync(CancellationToken.None);
Loading

0 comments on commit 9e0bc7e

Please sign in to comment.