From 6c39058b3ac575b96fc8d8d89146e5877e0b59b5 Mon Sep 17 00:00:00 2001 From: Daniel Cazzulino Date: Mon, 3 Jul 2023 17:50:06 -0300 Subject: [PATCH] Use raw string literals if supported by the target language In the cases where the values go through .editorconfig (such as constants and project properties), this isn't necessary (or supported) right now since .editorconfig will truncate multi-line values Closes #243 --- src/Directory.props | 1 + .../AssemblyInfoGenerator.cs | 11 +++++++---- src/ThisAssembly.AssemblyInfo/CSharp.sbntxt | 8 +++++++- src/ThisAssembly.AssemblyInfo/Model.cs | 1 + .../ThisAssembly.AssemblyInfo.csproj | 4 ++++ src/ThisAssembly.Constants/CSharp.sbntxt | 9 ++++++++- .../ConstantsGenerator.cs | 19 ++++++++++++------- src/ThisAssembly.Constants/Model.cs | 1 + src/ThisAssembly.Metadata/CSharp.sbntxt | 10 +++++++++- .../MetadataGenerator.cs | 13 +++++++++---- src/ThisAssembly.Metadata/Model.cs | 1 + src/ThisAssembly.Project/CSharp.sbntxt | 8 +++++++- src/ThisAssembly.Project/Model.cs | 1 + .../ProjectPropertyGenerator.cs | 12 ++++++++---- src/ThisAssembly.Tests/Tests.cs | 9 +++++++-- .../ThisAssembly.Tests.csproj | 17 +++++++++++++++-- 16 files changed, 98 insertions(+), 27 deletions(-) diff --git a/src/Directory.props b/src/Directory.props index 8001e20b..85c325ad 100644 --- a/src/Directory.props +++ b/src/Directory.props @@ -17,6 +17,7 @@ true true + MSB3277;$(NoWarn) diff --git a/src/ThisAssembly.AssemblyInfo/AssemblyInfoGenerator.cs b/src/ThisAssembly.AssemblyInfo/AssemblyInfoGenerator.cs index debda663..4b2e495a 100644 --- a/src/ThisAssembly.AssemblyInfo/AssemblyInfoGenerator.cs +++ b/src/ThisAssembly.AssemblyInfo/AssemblyInfoGenerator.cs @@ -39,7 +39,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Collect(); context.RegisterSourceOutput( - metadata.Combine(context.CompilationProvider.Select((s, _) => s.Language)), + metadata.Combine(context.ParseOptionsProvider), GenerateSource); } @@ -66,12 +66,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return new KeyValuePair(key, value); } - static void GenerateSource(SourceProductionContext spc, (ImmutableArray> attributes, string language) arg2) + static void GenerateSource(SourceProductionContext spc, (ImmutableArray> attributes, ParseOptions parse) arg) { - var (attributes, language) = arg2; + var (attributes, parse) = arg; var model = new Model(attributes.ToList()); - var file = language.Replace("#", "Sharp") + ".sbntxt"; + if (parse is CSharpParseOptions cs && (int)cs.LanguageVersion >= 11) + model.RawStrings = true; + + var file = parse.Language.Replace("#", "Sharp") + ".sbntxt"; var template = Template.Parse(EmbeddedResource.GetContent(file), file); var output = template.Render(model, member => member.Name); diff --git a/src/ThisAssembly.AssemblyInfo/CSharp.sbntxt b/src/ThisAssembly.AssemblyInfo/CSharp.sbntxt index ada76244..d4e035e9 100644 --- a/src/ThisAssembly.AssemblyInfo/CSharp.sbntxt +++ b/src/ThisAssembly.AssemblyInfo/CSharp.sbntxt @@ -24,8 +24,14 @@ partial class ThisAssembly public static partial class Info { {{~ for prop in Properties ~}} + {{~ if RawStrings ~}} + public const string {{ prop.Key }} = +""" +{{ prop.Value }} +"""; + {{~ else ~}} public const string {{ prop.Key }} = @"{{ prop.Value }}"; - + {{~ end ~}} {{~ end ~}} } } \ No newline at end of file diff --git a/src/ThisAssembly.AssemblyInfo/Model.cs b/src/ThisAssembly.AssemblyInfo/Model.cs index 13adbc84..0e00aef9 100644 --- a/src/ThisAssembly.AssemblyInfo/Model.cs +++ b/src/ThisAssembly.AssemblyInfo/Model.cs @@ -8,6 +8,7 @@ public class Model { public Model(IEnumerable> properties) => Properties = properties.ToList(); + public bool RawStrings { get; set; } = false; public string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); public List> Properties { get; } diff --git a/src/ThisAssembly.AssemblyInfo/ThisAssembly.AssemblyInfo.csproj b/src/ThisAssembly.AssemblyInfo/ThisAssembly.AssemblyInfo.csproj index 495918d9..4ede1e69 100644 --- a/src/ThisAssembly.AssemblyInfo/ThisAssembly.AssemblyInfo.csproj +++ b/src/ThisAssembly.AssemblyInfo/ThisAssembly.AssemblyInfo.csproj @@ -39,4 +39,8 @@ on the `ThisAssembly.Info` class. + + + + diff --git a/src/ThisAssembly.Constants/CSharp.sbntxt b/src/ThisAssembly.Constants/CSharp.sbntxt index e0d5f4c1..888927d2 100644 --- a/src/ThisAssembly.Constants/CSharp.sbntxt +++ b/src/ThisAssembly.Constants/CSharp.sbntxt @@ -22,7 +22,14 @@ { {{~ for value in $0.Values ~}} {{- summary value ~}} - public const string {{ value.Name | string.replace "-" "_" | string.replace " " "_" }} = @"{{ value.Value }}"; + {{~ if RawStrings ~}} + public const string {{ value.Name | string.replace "-" "_" | string.replace " " "_" }} = +""" +{{ value.Value }} +"""; + {{~ else ~}} + public const string {{ value.Name | string.replace "-" "_" | string.replace " " "_" }} = @"{{ value.Value }}"; + {{~ end ~}} {{~ end ~}} {{ for area in $0.NestedAreas diff --git a/src/ThisAssembly.Constants/ConstantsGenerator.cs b/src/ThisAssembly.Constants/ConstantsGenerator.cs index 7cdfd9ff..b3a17956 100644 --- a/src/ThisAssembly.Constants/ConstantsGenerator.cs +++ b/src/ThisAssembly.Constants/ConstantsGenerator.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Scriban; @@ -30,7 +31,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) comment: string.IsNullOrWhiteSpace(comment) ? null : comment, root: string.IsNullOrWhiteSpace(root) ? "Constants" : root!); }) - .Combine(context.CompilationProvider.Select((p, _) => p.Language)); + .Combine(context.ParseOptionsProvider); context.RegisterSourceOutput( files, @@ -38,20 +39,24 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } - void GenerateConstant(SourceProductionContext spc, ((string name, string value, string? comment, string root), string language) args) + void GenerateConstant(SourceProductionContext spc, ((string name, string value, string? comment, string root), ParseOptions parse) args) { - var ((name, value, comment, root), language) = args; + var ((name, value, comment, root), parse) = args; var rootArea = Area.Load(new List { new Constant(name, value, comment), }, root); - var file = language.Replace("#", "Sharp") + ".sbntxt"; + var file = parse.Language.Replace("#", "Sharp") + ".sbntxt"; var template = Template.Parse(EmbeddedResource.GetContent(file), file); - var output = template.Render(new Model(rootArea), member => member.Name); + var model = new Model(rootArea); + if (parse is CSharpParseOptions cs && (int)cs.LanguageVersion >= 11) + model.RawStrings = true; + + var output = template.Render(model, member => member.Name); // Apply formatting since indenting isn't that nice in Scriban when rendering nested // structures via functions. - if (language == LanguageNames.CSharp) + if (parse.Language == LanguageNames.CSharp) { - output = Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseCompilationUnit(output) + output = SyntaxFactory.ParseCompilationUnit(output) .NormalizeWhitespace() .GetText() .ToString(); diff --git a/src/ThisAssembly.Constants/Model.cs b/src/ThisAssembly.Constants/Model.cs index b23cd034..25ec0b69 100644 --- a/src/ThisAssembly.Constants/Model.cs +++ b/src/ThisAssembly.Constants/Model.cs @@ -7,6 +7,7 @@ [DebuggerDisplay("Values = {RootArea.Values.Count}")] record Model(Area RootArea) { + public bool RawStrings { get; set; } = false; public string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); } diff --git a/src/ThisAssembly.Metadata/CSharp.sbntxt b/src/ThisAssembly.Metadata/CSharp.sbntxt index 1c8c01b5..bfcdd8d8 100644 --- a/src/ThisAssembly.Metadata/CSharp.sbntxt +++ b/src/ThisAssembly.Metadata/CSharp.sbntxt @@ -24,8 +24,16 @@ partial class ThisAssembly public static partial class Metadata { {{~ for md in Metadata ~}} - /// {{ md.Key }} = {{ md.Value }} + /// {{ md.Key }} = {{ md.Value | string.split "\n" | array.first | string.replace "\r" "" | string.lstrip | string.rstrip }} + {{~ if RawStrings ~}} + public const string {{ md.Key }} = +""" +{{ md.Value }} +"""; + {{~ else ~}} + /// {{ prop.Key }} = {{ prop.Value }} public const string {{ md.Key }} = @"{{ md.Value }}"; + {{~ end ~}} {{~ end ~}} } diff --git a/src/ThisAssembly.Metadata/MetadataGenerator.cs b/src/ThisAssembly.Metadata/MetadataGenerator.cs index 89136d4f..6c9a8755 100644 --- a/src/ThisAssembly.Metadata/MetadataGenerator.cs +++ b/src/ThisAssembly.Metadata/MetadataGenerator.cs @@ -6,9 +6,11 @@ using System.Threading; using System.Xml.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; using Scriban; +using Scriban.Parsing; namespace ThisAssembly { @@ -26,7 +28,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Collect(); context.RegisterSourceOutput( - metadata.Combine(context.CompilationProvider.Select((s, _) => s.Language)), + metadata.Combine(context.ParseOptionsProvider), GenerateSource); } @@ -54,12 +56,15 @@ public void Initialize(IncrementalGeneratorInitializationContext context) return new KeyValuePair(key, value); } - void GenerateSource(SourceProductionContext spc, (ImmutableArray> attributes, string language) arg2) + void GenerateSource(SourceProductionContext spc, (ImmutableArray> attributes, ParseOptions parse) arg) { - var (attributes, language) = arg2; + var (attributes, parse) = arg; var model = new Model(attributes.ToList()); - var file = language.Replace("#", "Sharp") + ".sbntxt"; + if (parse is CSharpParseOptions cs && (int)cs.LanguageVersion >= 11) + model.RawStrings = true; + + var file = parse.Language.Replace("#", "Sharp") + ".sbntxt"; var template = Template.Parse(EmbeddedResource.GetContent(file), file); var output = template.Render(model, member => member.Name); diff --git a/src/ThisAssembly.Metadata/Model.cs b/src/ThisAssembly.Metadata/Model.cs index 3b76bff1..9ded172b 100644 --- a/src/ThisAssembly.Metadata/Model.cs +++ b/src/ThisAssembly.Metadata/Model.cs @@ -8,6 +8,7 @@ public class Model { public Model(IEnumerable> metadata) => Metadata = metadata.ToList(); + public bool RawStrings { get; set; } = false; public string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); public List> Metadata { get; } diff --git a/src/ThisAssembly.Project/CSharp.sbntxt b/src/ThisAssembly.Project/CSharp.sbntxt index 70f50423..b01110e5 100644 --- a/src/ThisAssembly.Project/CSharp.sbntxt +++ b/src/ThisAssembly.Project/CSharp.sbntxt @@ -24,9 +24,15 @@ partial class ThisAssembly public static partial class Project { {{~ for prop in Properties ~}} + {{~ if RawStrings ~}} + public const string {{ prop.Key }} = +""" +{{ prop.Value }} +"""; + {{~ else ~}} /// {{ prop.Key }} = {{ prop.Value }} public const string {{ prop.Key }} = @"{{ prop.Value }}"; - + {{~ end ~}} {{~ end ~}} } } \ No newline at end of file diff --git a/src/ThisAssembly.Project/Model.cs b/src/ThisAssembly.Project/Model.cs index 13adbc84..0e00aef9 100644 --- a/src/ThisAssembly.Project/Model.cs +++ b/src/ThisAssembly.Project/Model.cs @@ -8,6 +8,7 @@ public class Model { public Model(IEnumerable> properties) => Properties = properties.ToList(); + public bool RawStrings { get; set; } = false; public string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString(3); public List> Properties { get; } diff --git a/src/ThisAssembly.Project/ProjectPropertyGenerator.cs b/src/ThisAssembly.Project/ProjectPropertyGenerator.cs index 1519aac3..5a0500cf 100644 --- a/src/ThisAssembly.Project/ProjectPropertyGenerator.cs +++ b/src/ThisAssembly.Project/ProjectPropertyGenerator.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Text; using Scriban; @@ -30,16 +31,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Collect(); context.RegisterSourceOutput( - properties.Combine(context.CompilationProvider.Select((s, _) => s.Language)), + properties.Combine(context.ParseOptionsProvider), GenerateSource); } - void GenerateSource(SourceProductionContext spc, (ImmutableArray> properties, string language) arg2) + void GenerateSource(SourceProductionContext spc, (ImmutableArray> properties, ParseOptions parse) arg) { - var (properties, language) = arg2; + var (properties, parse) = arg; var model = new Model(properties); - var file = language.Replace("#", "Sharp") + ".sbntxt"; + if (parse is CSharpParseOptions cs && (int)cs.LanguageVersion >= 11) + model.RawStrings = true; + + var file = parse.Language.Replace("#", "Sharp") + ".sbntxt"; var template = Template.Parse(EmbeddedResource.GetContent(file), file); var output = template.Render(model, member => member.Name); diff --git a/src/ThisAssembly.Tests/Tests.cs b/src/ThisAssembly.Tests/Tests.cs index fd7ff9a4..7a11d414 100644 --- a/src/ThisAssembly.Tests/Tests.cs +++ b/src/ThisAssembly.Tests/Tests.cs @@ -19,8 +19,13 @@ public void CanUseInfo() [Fact] public void CanUseInfoDescription() - => Assert.Equal(@"A Description -with a newline".ReplaceLineEndings(), ThisAssembly.Info.Description.ReplaceLineEndings()); + => Assert.Equal( + """ + A Description + with a newline and + * Some "things" with quotes + // Some comments too. + """.ReplaceLineEndings(), ThisAssembly.Info.Description.ReplaceLineEndings()); [Fact] public void CanUseConstants() diff --git a/src/ThisAssembly.Tests/ThisAssembly.Tests.csproj b/src/ThisAssembly.Tests/ThisAssembly.Tests.csproj index d13cbd19..b20836f1 100644 --- a/src/ThisAssembly.Tests/ThisAssembly.Tests.csproj +++ b/src/ThisAssembly.Tests/ThisAssembly.Tests.csproj @@ -3,8 +3,16 @@ false net7.0 + + A Description + with a newline and + * Some "things" with quotes + // Some comments too. + A Description -with a newline + with a newline and + * Some "things" with quotes + // Some comments too. net472 ThisAssemblyTests true @@ -54,6 +62,10 @@ with a newline + + + + @@ -61,13 +73,14 @@ with a newline Included/%(Filename)%(Extension) + - +