Skip to content

Commit

Permalink
Since types can be public, ensure we always have XML comments
Browse files Browse the repository at this point in the history
This is important in particular when we add intermediate automatic "area"
classes (such as when a constant is Foo.Bar.Baz), since in those cases,
the user has no way of specifying a comment to fix the issue.
  • Loading branch information
kzu committed Oct 9, 2024
1 parent a316274 commit 9f94990
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 20 deletions.
6 changes: 3 additions & 3 deletions src/ThisAssembly.Constants/CSharp.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
{{~ end ~}}
{{ end }}
{{ func render }}
/// <summary>
/// {{ $0.Comment }}
/// </summary>
public static partial class {{ $0.Name | string.replace "-" "_" | string.replace " " "_" }}
{
{{~ for value in $0.Values ~}}
Expand Down Expand Up @@ -73,8 +76,5 @@ namespace {{ Namespace }};
/// </summary>
{{ Visibility }}partial class ThisAssembly
{
/// <summary>
/// {{ RootArea.Comment }}
/// </summary>
{{ render RootArea }}
}
25 changes: 20 additions & 5 deletions src/ThisAssembly.Constants/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CSharp;

namespace ThisAssembly;
Expand All @@ -22,10 +23,21 @@ record Model(Area RootArea, string? Namespace, bool IsPublic)
}

[DebuggerDisplay("Name = {Name}, NestedAreas = {NestedAreas.Count}, Values = {Values.Count}")]
record Area(string Name, string Prefix, string Comment)
record Area(string Name, string Prefix)
{
public List<Area> NestedAreas { get; init; } = new();
public List<Constant> Values { get; init; } = new();
string? comment = null;
Area? parent = null;

public string Comment
{
get => comment ?? $"Provides access to constants under {Path}";
set => comment = value;
}

public string Path => parent == null ? Name : $"{parent.Path}/{Name}";

public List<Area> NestedAreas { get; init; } = [];
public List<Constant> Values { get; init; } = [];

static string EscapeIdentifier(string identifier)
{
Expand All @@ -50,7 +62,7 @@ static string EscapeIdentifier(string identifier)

public static Area Load(List<Constant> constants, string rootArea = "Constants", string comment = "Provides access project-defined constants.")
{
var root = new Area(rootArea, "", comment);
var root = new Area(rootArea, "") { Comment = comment };

foreach (var constant in constants)
{
Expand Down Expand Up @@ -96,7 +108,10 @@ static Area GetArea(Area area, IEnumerable<string> areaPath)
"Area name '{0}' is already in use as a value name under area '{1}'.",
areaName, currentArea.Name));

existing = new Area(areaName, currentArea.Prefix + areaName + ".", "");
existing = new Area(areaName, currentArea.Prefix + areaName + ".")
{
parent = currentArea
};
currentArea.NestedAreas.Add(existing);
}

Expand Down
17 changes: 14 additions & 3 deletions src/ThisAssembly.Resources/CSharp.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,31 @@
{
{{~ if $0.IsText ~}}
private static string text;

/// <summary>
/// Gets the resource as plain text.
/// </summary>
public static string Text =>
text ??= EmbeddedResource.GetContent(@"{{ $0.Path }}");
{{~ end ~}}

/// <summary>
/// Gets the resource as a byte array.
/// </summary>
public static byte[] GetBytes() =>
EmbeddedResource.GetBytes(@"{{ $0.Path }}");

/// <summary>
/// Gets the resource as a stream.
/// </summary>
public static Stream GetStream() =>
EmbeddedResource.GetStream(@"{{ $0.Path }}");
}
{{ end }}
{{ func render }}
/// <summary>
/// {{ $0.Comment }}
/// </summary>
public static partial class {{ $0.Name | string.replace "-" "_" | string.replace " " "_" }}
{
{{~ if $0.Resources ~}}
Expand All @@ -65,8 +79,5 @@ namespace {{ Namespace }};
/// </summary>
{{ Visibility }}partial class ThisAssembly
{
/// <summary>
/// Provides access to assembly embedded resources.
/// </summary>
{{ render RootArea }}
}
32 changes: 26 additions & 6 deletions src/ThisAssembly.Resources/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,34 @@ record Model(Area RootArea, string? Namespace, bool IsPublic)
[DebuggerDisplay("Name = {Name}")]
record Area(string Name)
{
public Area? NestedArea { get; private set; }
Area? nestedArea = null;
Area? parent = null;
string? comment = null;

public string? Comment
{
get => comment ?? $"Provides access to embedded resources under {Path}";
set => comment = value;
}

public Area? NestedArea
{
get => nestedArea;
set
{
nestedArea = value;
if (nestedArea != null)
nestedArea.parent = this;
}
}

public string Path => parent == null ? Name : $"{parent.Path}/{Name}";

public IEnumerable<Resource>? Resources { get; private set; }

public static Area Load(string basePath, List<Resource> resources, string rootArea = "Resources")
public static Area Load(string basePath, List<Resource> resources, string rootArea = "Resources", string comment = "Provides access to embedded resources.")
{
var root = new Area(rootArea);
var root = new Area(rootArea) { Comment = comment };

// Splits: ([area].)*[name]
var area = root;
Expand All @@ -36,9 +58,7 @@ public static Area Load(string basePath, List<Resource> resources, string rootAr
{
var partStr = PathSanitizer.Sanitize(part, parent);
parent = partStr;

area.NestedArea = new Area(partStr);
area = area.NestedArea;
area = area.NestedArea = new Area(partStr);
}

area.Resources = resources
Expand Down
2 changes: 1 addition & 1 deletion src/ThisAssembly.Resources/ResourcesGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
{
if (!p.GlobalOptions.TryGetValue("build_property.EmbeddedResourceStringExtensions", out var extensions) ||
extensions == null)
return Array.Empty<string>();
return [];
return extensions.Split('|');
})
Expand Down
1 change: 1 addition & 0 deletions src/ThisAssembly.Strings/CSharp.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
{{~ end ~}}
{{ end }}
{{ func render }}
/// <summary />
public static partial class {{ $0.Id }}
{
{{~ for value in $0.Values }}
Expand Down
3 changes: 3 additions & 0 deletions src/ThisAssembly.Tests/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;

/// <summary />
public static class Extensions
{
/// <summary />
public static string ReplaceLineEndings(this string input) => ReplaceLineEndings(input, Environment.NewLine);

/// <summary />
public static string ReplaceLineEndings(this string input, string replacementText)
{
#if NET6_0_OR_GREATER
Expand Down
2 changes: 2 additions & 0 deletions src/ThisAssembly.Tests/ScribanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@

namespace ThisAssemblyTests;

/// <summary />
public class ScribanTests(ITestOutputHelper Console)
{
/// <summary />
[Fact]
public void CanRenderModel()
{
Expand Down
31 changes: 31 additions & 0 deletions src/ThisAssembly.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@

namespace ThisAssemblyTests;

/// <summary />
public record class Tests(ITestOutputHelper Output)
{
/// <summary />
[Fact]
public void CanReadResourceFile()
=> Assert.NotNull(ResourceFile.Load("Resources.resx", "Strings"));

/// <summary />
[Fact]
public void CanUseInfo()
=> Assert.Equal("ThisAssembly.Tests", ThisAssembly.Info.Title);

/// <summary />
[Fact]
public void CanUseInfoDescription()
=> Assert.Equal(
Expand All @@ -29,6 +33,7 @@ with a newline and
// Some comments too.
""".ReplaceLineEndings(), ThisAssembly.Info.Description.ReplaceLineEndings());

/// <summary />
[Fact]
public void CanUseMultilineProjectProperty()
=> Assert.Equal(
Expand All @@ -39,112 +44,138 @@ with a newline and
// Some comments too.
""".ReplaceLineEndings(), ThisAssembly.Project.Multiline.ReplaceLineEndings());

/// <summary />
[Fact]
public void CanUseProjectFullFileContents()
{
Assert.NotEmpty(ThisAssembly.Project.ProjectFile);
Assert.False(ThisAssembly.Project.ProjectFile.StartsWith("|"));
}

/// <summary />
[Fact]
public void CanUseConstants()
=> Assert.Equal("Baz", ThisAssembly.Constants.Foo.Bar);

/// <summary />
[Fact]
public void CanUseTypedIntConstant()
=> Assert.Equal(123, ThisAssembly.Constants.TypedInt);

/// <summary />
[Fact]
public void CanUseTypedInt64Constant()
=> Assert.Equal(123, ThisAssembly.Constants.TypedInt64);

/// <summary />
[Fact]
public void CanUseTypedLongConstant()
=> Assert.Equal(123, ThisAssembly.Constants.TypedLong);

/// <summary />
[Fact]
public void CanUseTypedBoolConstant()
=> Assert.True(ThisAssembly.Constants.TypedBoolean);

/// <summary />
[Fact]
public void CanUseTypedDoubleConstant()
=> Assert.Equal(1.23, ThisAssembly.Constants.TypedDouble);

/// <summary />
[Fact]
public void CanUseTypedTimeSpanStaticProp()
=> Assert.Equal(TimeSpan.FromSeconds(5), ThisAssembly.Constants.TypedTimeSpan);

/// <summary />
[Fact]
public void CanUseFileConstants()
=> Assert.Equal(ThisAssembly.Constants.Content.Docs.License, Path.Combine("Content", "Docs", "License.md"));

/// <summary />
[Fact]
public void CanUseFileConstantInvalidIdentifier()
=> Assert.Equal(ThisAssembly.Constants.Content.Docs._12._Readme_copy_, Path.Combine("Content", "Docs", "12. Readme (copy).txt"));

/// <summary />
[Fact]
public void CanUseFileConstantLinkedFile()
=> Assert.Equal(ThisAssembly.Constants.Included.Readme, Path.Combine("Included", "Readme.txt"));

/// <summary />
[Fact]
public void CanUseMetadata()
=> Assert.Equal("Bar", ThisAssembly.Metadata.Foo);

/// <summary />
[Fact]
public void CanUseHierarchicalMetadata()
=> Assert.Equal("Baz", ThisAssembly.Metadata.Root.Foo.Bar);

/// <summary />
[Fact]
public void CanUseProjectProperty()
=> Assert.Equal("Bar", ThisAssembly.Project.Foo);

/// <summary />
[Fact]
public void CanUseStringsNamedArguments()
=> Assert.NotNull(ThisAssembly.Strings.Named("hello", "world"));

/// <summary />
[Fact]
public void CanUseStringsIndexedArguments()
=> Assert.NotNull(ThisAssembly.Strings.Indexed("hello", "world"));

/// <summary />
[Fact]
public void CanUseStringsNamedFormattedArguments()
=> Assert.Equal("Year 2020, Month 03", ThisAssembly.Strings.WithNamedFormat(new DateTime(2020, 3, 20)));

/// <summary />
[Fact]
public void CanUseStringsIndexedFormattedArguments()
=> Assert.Equal("Year 2020, Month 03", ThisAssembly.Strings.WithIndexedFormat(new DateTime(2020, 3, 20)));

/// <summary />
[Fact]
public void CanUseStringResource()
=> Assert.Equal("Value", ThisAssembly.Strings.Foo.Bar.Baz);

/// <summary />
[Fact]
public void CanUseTextResource()
=> Assert.NotNull(ThisAssembly.Resources.Content.Styles.Custom.Text);

/// <summary />
[Fact]
public void CanUseByteResource()
=> Assert.NotNull(ThisAssembly.Resources.Content.Styles.Main.GetBytes());

/// <summary />
[Fact]
public void CanUseSameNameDifferentExtensions()
=> Assert.NotNull(ThisAssembly.Resources.Content.Swagger.swagger_ui.css.GetBytes());

/// <summary />
[Fact]
public void CanUseFileInvalidCharacters()
=> Assert.NotNull(ThisAssembly.Resources.webhook_data.Text);

/// <summary />
[Fact]
public void CanUseGitConstants()
=> Assert.NotEmpty(ThisAssembly.Git.Commit);

/// <summary />
[Fact]
public void CanUseGitBranchConstants()
{
Assert.NotEmpty(ThisAssembly.Git.Branch);
Output.WriteLine(ThisAssembly.Git.Branch);
}

/// <summary />
[Fact]
public void CanUseSemicolonsInConstant()
=> Assert.Equal("A;B;C", ThisAssembly.Constants.WithSemiColon);
Expand Down
6 changes: 4 additions & 2 deletions src/ThisAssembly.Tests/ThisAssembly.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
// Some comments too.</Description>
<TargetFramework Condition="'$(BuildingInsideVisualStudio)' == 'true'">net472</TargetFramework>
<RootNamespace>ThisAssemblyTests</RootNamespace>
<!--<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>-->
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<NoWarn>CS0618;CS8981;TA100;$(NoWarn)</NoWarn>
<PackageScribanIncludeSource>false</PackageScribanIncludeSource>
<ProjectFile>$([System.IO.File]::ReadAllText($(MSBuildProjectFullPath)))</ProjectFile>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<WarningsAsErrors>true</WarningsAsErrors>
</PropertyGroup>

<Import Project="..\*\ThisAssembly*.props" />
Expand Down Expand Up @@ -69,7 +71,7 @@
<ProjectProperty Include="Foo" />
<ProjectProperty Include="Description" />
<ProjectProperty Include="Multiline" />
<ProjectProperty Include="ProjectFile" Comment="Full project contents" />
<ProjectProperty Include="ProjectFile" />
<Constant Include="Foo.Raw" Value="$(Multiline)" Comment="$(Multiline)" />
<Constant Include="Foo.Bar" Value="Baz" Comment="Yay!" />
<Constant Include="Foo.Hello" Value="World" Comment="Comments make everything better 😍" />
Expand Down

0 comments on commit 9f94990

Please sign in to comment.