diff --git a/src/ThisAssembly.Constants/ConstantsGenerator.cs b/src/ThisAssembly.Constants/ConstantsGenerator.cs index 1c15ed7c..2fcb9046 100644 --- a/src/ThisAssembly.Constants/ConstantsGenerator.cs +++ b/src/ThisAssembly.Constants/ConstantsGenerator.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Devlooped.Sponsors; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -16,6 +17,8 @@ namespace ThisAssembly; [Generator(LanguageNames.CSharp)] public class ConstantsGenerator : IIncrementalGenerator { + static readonly Regex SeeExpr = new("", RegexOptions.Compiled); + public void Initialize(IncrementalGeneratorInitializationContext context) { var files = context.AdditionalTextsProvider @@ -121,12 +124,16 @@ void GenerateConstant(SourceProductionContext spc, // structures via functions. if (parse.Language == LanguageNames.CSharp) { - output = SyntaxFactory.ParseCompilationUnit(output, options: cs) + output = SeeExpr.Replace(SyntaxFactory + .ParseCompilationUnit(output, options: cs) .NormalizeWhitespace() .GetText() - .ToString(); + .ToString(), + $""); } spc.AddSource($"{root}.{name}.g.cs", SourceText.From(output, Encoding.UTF8)); } + + } diff --git a/src/ThisAssembly.Resources/CSharp.sbntxt b/src/ThisAssembly.Resources/CSharp.sbntxt index 419a262c..82b9c55d 100644 --- a/src/ThisAssembly.Resources/CSharp.sbntxt +++ b/src/ThisAssembly.Resources/CSharp.sbntxt @@ -57,5 +57,16 @@ namespace {{ Namespace }}; /// /// Provides access to assembly resources. /// + {{~ if Remarks ~}} + {{ Remarks }} + /// + {{~ end ~}} + {{~ if Warn ~}} + [Obsolete("{{ Warn }}", false + #if NET6_0_OR_GREATER + , UrlFormat = "{{ Url }}" + #endif + )] + {{~ end ~}} {{ render RootArea }} } diff --git a/src/ThisAssembly.Resources/Model.cs b/src/ThisAssembly.Resources/Model.cs index d2e69139..4652e2af 100644 --- a/src/ThisAssembly.Resources/Model.cs +++ b/src/ThisAssembly.Resources/Model.cs @@ -9,6 +9,9 @@ namespace ThisAssembly; [DebuggerDisplay("Values = {RootArea.Values.Count}")] record Model(Area RootArea, string? Namespace, bool IsPublic) { + public string? Warn { get; set; } + public string? Remarks { get; set; } + public string Url => Devlooped.Sponsors.SponsorLink.Funding.HelpUrl; public string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); public string Visibility => IsPublic ? "public " : ""; } diff --git a/src/ThisAssembly.Resources/ResourcesGenerator.cs b/src/ThisAssembly.Resources/ResourcesGenerator.cs index 8e2ff04a..7b5ce772 100644 --- a/src/ThisAssembly.Resources/ResourcesGenerator.cs +++ b/src/ThisAssembly.Resources/ResourcesGenerator.cs @@ -1,17 +1,24 @@ using System; using System.Collections.Immutable; +using System.Globalization; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; +using Devlooped.Sponsors; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Scriban; +using static Devlooped.Sponsors.SponsorLink; namespace ThisAssembly; [Generator(LanguageNames.CSharp)] public class ResourcesGenerator : IIncrementalGenerator { + static readonly Regex SeeExpr = new("", RegexOptions.Compiled); + public void Initialize(IncrementalGeneratorInitializationContext context) { context.RegisterPostInitializationOutput( @@ -52,16 +59,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context) c.GlobalOptions.TryGetValue("build_property.ThisAssemblyVisibility", out var visibility) && !string.IsNullOrEmpty(visibility) ? visibility : null )); + // this is required to ensure status is registered properly independently of analyzer runs. + var options = context.GetStatusOptions(); + context.RegisterSourceOutput( - files.Combine(right), + files.Combine(right).Combine(options.Combine(context.ParseOptionsProvider)), GenerateSource); } static void GenerateSource(SourceProductionContext spc, - ((ImmutableArray<(string resourceName, string? kind, string? comment)> files, - ImmutableArray extensions), (string? ns, string? visibility)) args) + (((ImmutableArray<(string resourceName, string? kind, string? comment)> files, + ImmutableArray extensions), (string? ns, string? visibility)), (StatusOptions options, ParseOptions parse)) args) { - var ((files, extensions), (ns, visibility)) = args; + var (((files, extensions), (ns, visibility)), (options, parse)) = args; var file = "CSharp.sbntxt"; var template = Template.Parse(EmbeddedResource.GetContent(file), file); @@ -70,6 +80,23 @@ static void GenerateSource(SourceProductionContext spc, .GroupBy(f => Path.Combine( Path.GetDirectoryName(f.resourceName), Path.GetFileNameWithoutExtension(f.resourceName))); + + string? warn = default; + string? remarks = default; + if (IsEditor) + { + var status = Diagnostics.GetOrSetStatus(options); + if (status == SponsorStatus.Unknown || status == SponsorStatus.Expired) + { + warn = string.Format(CultureInfo.CurrentCulture, Resources.Editor_Disabled, Funding.Product, Funding.HelpUrl); + remarks = Resources.Editor_DisabledRemarks; + } + else if (status == SponsorStatus.Grace && Diagnostics.TryGet() is { } grace && grace.Properties.TryGetValue(nameof(SponsorStatus.Grace), out var days)) + { + remarks = string.Format(CultureInfo.CurrentCulture, Resources.Editor_GraceRemarks, days); + } + } + foreach (var group in groupsWithoutExtensions) { var basePath = group.Key; @@ -90,16 +117,19 @@ static void GenerateSource(SourceProductionContext spc, .ToList(); var root = Area.Load(basePath, resources); - var model = new Model(root, ns, "public".Equals(visibility, StringComparison.OrdinalIgnoreCase)); - + var model = new Model(root, ns, "public".Equals(visibility, StringComparison.OrdinalIgnoreCase)) + { + Warn = warn, + Remarks = remarks, + }; var output = template.Render(model, member => member.Name); - // Apply formatting since indenting isn't that nice in Scriban when rendering nested - // structures via functions. - output = Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseCompilationUnit(output) + output = SeeExpr.Replace(SyntaxFactory + .ParseCompilationUnit(output, options: parse as CSharpParseOptions) .NormalizeWhitespace() .GetText() - .ToString(); + .ToString(), + $""); spc.AddSource( $"{basePath.Replace('\\', '.').Replace('/', '.')}.g.cs", diff --git a/src/ThisAssembly.Resources/ThisAssembly.Resources.props b/src/ThisAssembly.Resources/ThisAssembly.Resources.props index 16795a20..ca8ce68b 100644 --- a/src/ThisAssembly.Resources/ThisAssembly.Resources.props +++ b/src/ThisAssembly.Resources/ThisAssembly.Resources.props @@ -1,7 +1,7 @@ - .txt|.cs|.sql|.json|.md + .txt|.cs|.sql|.json|.md|.resx diff --git a/src/ThisAssembly.Strings/StringsGenerator.cs b/src/ThisAssembly.Strings/StringsGenerator.cs index 5bf298e3..7c4fdebb 100644 --- a/src/ThisAssembly.Strings/StringsGenerator.cs +++ b/src/ThisAssembly.Strings/StringsGenerator.cs @@ -2,8 +2,8 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Resources; using System.Text; +using System.Text.RegularExpressions; using Devlooped.Sponsors; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -16,6 +16,8 @@ namespace ThisAssembly; [Generator(LanguageNames.CSharp)] public class StringsGenerator : IIncrementalGenerator { + static readonly Regex SeeExpr = new("", RegexOptions.Compiled); + public void Initialize(IncrementalGeneratorInitializationContext context) { // Read the ThisAssemblyNamespace property or default to null @@ -83,10 +85,12 @@ static void GenerateSource(SourceProductionContext spc, var output = template.Render(model, member => member.Name); - output = SyntaxFactory.ParseCompilationUnit(output, options: parse as CSharpParseOptions) + output = SeeExpr.Replace(SyntaxFactory + .ParseCompilationUnit(output, options: parse as CSharpParseOptions) .NormalizeWhitespace() .GetText() - .ToString(); + .ToString(), + $""); spc.AddSource(resourceName, SourceText.From(output, Encoding.UTF8)); }