From ee13443205f8ed68dcc6dce87687f4cb341dde27 Mon Sep 17 00:00:00 2001 From: Poker Date: Fri, 15 Apr 2022 22:09:36 +0800 Subject: [PATCH] optimize source generator delegate --- src/Pixeval.SourceGen/DependencyProperty.cs | 28 +++- src/Pixeval.SourceGen/GenerateConstructor.cs | 25 ++- .../LoadSaveConfiguration.cs | 144 ++++++++++-------- src/Pixeval.SourceGen/SettingsViewModel.cs | 77 +++++----- .../TypeWithAttributeGenerator.cs | 27 +++- src/Pixeval.SourceGen/Utils.cs | 24 ++- 6 files changed, 214 insertions(+), 111 deletions(-) diff --git a/src/Pixeval.SourceGen/DependencyProperty.cs b/src/Pixeval.SourceGen/DependencyProperty.cs index 9a8da1772..08884959e 100644 --- a/src/Pixeval.SourceGen/DependencyProperty.cs +++ b/src/Pixeval.SourceGen/DependencyProperty.cs @@ -1,8 +1,28 @@ -using System; +#region Copyright (c) Pixeval/Pixeval.SourceGen + +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2021 Pixeval.SourceGen/LoadSaveConfigurationGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; -using System.Linq; using System.Text; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; using static Pixeval.SourceGen.Utils; @@ -11,13 +31,13 @@ namespace Pixeval.SourceGen; internal static partial class TypeWithAttributeDelegates { - public static string? DependencyProperty(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, Func attributeEqualityComparer) + public static string? DependencyProperty(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, List attributeList) { var members = new List(); var namespaces = new HashSet { "Microsoft.UI.Xaml" }; var usedTypes = new HashSet(SymbolEqualityComparer.Default); - foreach (var attribute in typeSymbol.GetAttributes().Where(attributeEqualityComparer)) + foreach (var attribute in attributeList) { if (attribute.ConstructorArguments[0].Value is not string propertyName || attribute.ConstructorArguments[1].Value is not INamedTypeSymbol type) continue; diff --git a/src/Pixeval.SourceGen/GenerateConstructor.cs b/src/Pixeval.SourceGen/GenerateConstructor.cs index 90d0fb7f6..a5531584d 100644 --- a/src/Pixeval.SourceGen/GenerateConstructor.cs +++ b/src/Pixeval.SourceGen/GenerateConstructor.cs @@ -1,4 +1,25 @@ -using System; +#region Copyright (c) Pixeval/Pixeval.SourceGen + +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2021 Pixeval.SourceGen/LoadSaveConfigurationGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -12,7 +33,7 @@ namespace Pixeval.SourceGen; internal static partial class TypeWithAttributeDelegates { - public static string GenerateConstructor(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, Func attributeEqualityComparer) + public static string GenerateConstructor(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, List attributeList) { var name = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); var namespaces = new HashSet(); diff --git a/src/Pixeval.SourceGen/LoadSaveConfiguration.cs b/src/Pixeval.SourceGen/LoadSaveConfiguration.cs index ac68a646b..ddf577943 100644 --- a/src/Pixeval.SourceGen/LoadSaveConfiguration.cs +++ b/src/Pixeval.SourceGen/LoadSaveConfiguration.cs @@ -1,61 +1,80 @@ -using Microsoft.CodeAnalysis; +#region Copyright (c) Pixeval/Pixeval.SourceGen + +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2021 Pixeval.SourceGen/LoadSaveConfigurationGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using static Pixeval.SourceGen.Utils; namespace Pixeval.SourceGen; - internal static partial class TypeWithAttributeDelegates { - public static string? LoadSaveConfiguration(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, Func attributeEqualityComparer) + public static string? LoadSaveConfiguration(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, List attributeList) { - foreach (var attribute in typeSymbol.GetAttributes().Where(attributeEqualityComparer)) - { - if (attribute.ConstructorArguments[0].Value is not INamedTypeSymbol type) - continue; - if (attribute.ConstructorArguments[1].Value is not string containerName) - continue; + var attribute = attributeList[0]; + if (attribute.ConstructorArguments[0].Value is not INamedTypeSymbol type) + return null; + if (attribute.ConstructorArguments[1].Value is not string containerName) + return null; - string? staticClassName = null; - string? methodName = null; + string? staticClassName = null; + string? methodName = null; - if (attribute.NamedArguments[0].Key is "CastMethod" && attribute.NamedArguments[0].Value.Value is { } value) - { - var castMethodFullName = (string)value; - var dotPosition = castMethodFullName.LastIndexOf('.'); - if (dotPosition is -1) - throw new InvalidDataException("\"CastMethod\" must contain the full name."); - staticClassName = "static " + castMethodFullName.Substring(0, dotPosition); - methodName = castMethodFullName.Substring(dotPosition + 1); - } + if (attribute.NamedArguments[0].Key is "CastMethod" && attribute.NamedArguments[0].Value.Value is { } value) + { + var castMethodFullName = (string)value; + var dotPosition = castMethodFullName.LastIndexOf('.'); + if (dotPosition is -1) + throw new InvalidDataException("\"CastMethod\" must contain the full name."); + staticClassName = "static " + castMethodFullName.Substring(0, dotPosition); + methodName = castMethodFullName.Substring(dotPosition + 1); + } - var name = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); - var namespaces = new HashSet(); - if (staticClassName is not null) - namespaces.Add(staticClassName);//methodName方法所用namespace - var usedTypes = new HashSet(SymbolEqualityComparer.Default); - /*-----Body Begin-----*/ - var stringBuilder = new StringBuilder().AppendLine("#nullable enable\n"); - /*-----Splitter-----*/ - var classBegin = @$" + var name = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + var namespaces = new HashSet(); + if (staticClassName is not null) + namespaces.Add(staticClassName); //methodName方法所用namespace + var usedTypes = new HashSet(SymbolEqualityComparer.Default); + /*-----Body Begin-----*/ + var stringBuilder = new StringBuilder().AppendLine("#nullable enable\n"); + /*-----Splitter-----*/ + var classBegin = @$" namespace {typeSymbol.ContainingNamespace.ToDisplayString()}; partial class {name} {{"; - var loadConfigurationBegin = $@" public static {type.Name}? LoadConfiguration() + var loadConfigurationBegin = $@" public static {type.Name}? LoadConfiguration() {{ try {{ return new {type.Name}("; - /*-----Splitter-----*/ - var loadConfigurationContent = new StringBuilder(); - /*-----Splitter-----*/ - var loadConfigurationEndAndSaveConfigurationBegin = $@" ); + /*-----Splitter-----*/ + var loadConfigurationContent = new StringBuilder(); + /*-----Splitter-----*/ + var loadConfigurationEndAndSaveConfigurationBegin = $@" ); }} catch {{ @@ -67,38 +86,37 @@ public static void SaveConfiguration({type.Name}? configuration) {{ if (configuration is {{ }} appConfiguration) {{"; - /*-----Splitter-----*/ - var saveConfigurationContent = new StringBuilder(); - /*-----Splitter-----*/ - const string saveConfigurationEndAndClassEnd = $@" }} + /*-----Splitter-----*/ + var saveConfigurationContent = new StringBuilder(); + /*-----Splitter-----*/ + const string saveConfigurationEndAndClassEnd = $@" }} }} }}"; - /*-----Body End-----*/ - foreach (var member in type.GetMembers().Where(member => - member is { Kind: SymbolKind.Property } and not { Name: "EqualityContract" }) - .Cast()) - { - loadConfigurationContent.AppendLine(LoadRecord(member.Name, member.Type.Name, type.Name, containerName, methodName)); - saveConfigurationContent.AppendLine(SaveRecord(member.Name, member.Type, type.Name, containerName, methodName)); - namespaces.UseNamespace(usedTypes, typeSymbol, member.Type); - } - - // 去除" \r\n" - loadConfigurationContent = loadConfigurationContent.Remove(loadConfigurationContent.Length - 3, 3); - - foreach (var s in namespaces) - _ = stringBuilder.AppendLine($"using {s};"); - stringBuilder.AppendLine(classBegin) - .AppendLine(loadConfigurationBegin) - .AppendLine(loadConfigurationContent.ToString()) - .AppendLine(loadConfigurationEndAndSaveConfigurationBegin) - // saveConfigurationContent 后已有空行 - .Append(saveConfigurationContent) - .AppendLine(saveConfigurationEndAndClassEnd); - return stringBuilder.ToString(); + /*-----Body End-----*/ + foreach (var member in type.GetMembers().Where(member => + member is { Kind: SymbolKind.Property } and not { Name: "EqualityContract" }) + .Cast()) + { + loadConfigurationContent.AppendLine(LoadRecord(member.Name, member.Type.Name, type.Name, containerName, + methodName)); + saveConfigurationContent.AppendLine(SaveRecord(member.Name, member.Type, type.Name, containerName, + methodName)); + namespaces.UseNamespace(usedTypes, typeSymbol, member.Type); } - return null; + // 去除" \r\n" + loadConfigurationContent = loadConfigurationContent.Remove(loadConfigurationContent.Length - 3, 3); + + foreach (var s in namespaces) + _ = stringBuilder.AppendLine($"using {s};"); + stringBuilder.AppendLine(classBegin) + .AppendLine(loadConfigurationBegin) + .AppendLine(loadConfigurationContent.ToString()) + .AppendLine(loadConfigurationEndAndSaveConfigurationBegin) + // saveConfigurationContent 后已有空行 + .Append(saveConfigurationContent) + .AppendLine(saveConfigurationEndAndClassEnd); + return stringBuilder.ToString(); } diff --git a/src/Pixeval.SourceGen/SettingsViewModel.cs b/src/Pixeval.SourceGen/SettingsViewModel.cs index 55d3456b0..860e48ed9 100644 --- a/src/Pixeval.SourceGen/SettingsViewModel.cs +++ b/src/Pixeval.SourceGen/SettingsViewModel.cs @@ -1,4 +1,5 @@ #region Copyright (c) Pixeval/Pixeval.SourceGen + // GPL v3 License // // Pixeval/Pixeval.SourceGen @@ -16,71 +17,71 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . + #endregion -using System; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Pixeval.SourceGen.Utils; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using static Pixeval.SourceGen.Utils; namespace Pixeval.SourceGen; internal static partial class TypeWithAttributeDelegates { - public static string? SettingsViewModel(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, Func attributeEqualityComparer) + public static string? SettingsViewModel(TypeDeclarationSyntax typeDeclaration, INamedTypeSymbol typeSymbol, + List attributeList) { - foreach (var typeAttribute in typeSymbol.GetAttributes().Where(attributeEqualityComparer)) - { - if (typeAttribute.ConstructorArguments[0].Value is not INamedTypeSymbol type) - continue; - if (typeAttribute.ConstructorArguments[1].Value is not string settingName) - continue; + var attribute = attributeList[0]; + if (attribute.ConstructorArguments[0].Value is not INamedTypeSymbol type) + return null; + if (attribute.ConstructorArguments[1].Value is not string settingName) + return null; - var name = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); - var namespaces = new HashSet { typeSymbol.ContainingNamespace.ToDisplayString() }; - var usedTypes = new HashSet(SymbolEqualityComparer.Default); - const string nullable = "#nullable enable\n"; - var classBegin = @$"namespace {typeSymbol.ContainingNamespace.ToDisplayString()}; + var name = typeSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + var namespaces = new HashSet { typeSymbol.ContainingNamespace.ToDisplayString() }; + var usedTypes = new HashSet(SymbolEqualityComparer.Default); + const string nullable = "#nullable enable\n"; + var classBegin = @$"namespace {typeSymbol.ContainingNamespace.ToDisplayString()}; partial class {name} {{"; - var propertySentences = new List(); - const string classEnd = @"}"; + var propertySentences = new List(); + const string classEnd = @"}"; - foreach (var property in type.GetMembers().Where(property => - property is { Kind: SymbolKind.Property } and not { Name: "EqualityContract" } - && !property.GetAttributes().Any(propertyAttribute => propertyAttribute.AttributeClass!.Name is "SettingsViewModelExclusionAttribute")) - .Cast()) + foreach (var property in type.GetMembers().Where(property => + property is { Kind: SymbolKind.Property } and not { Name: "EqualityContract" } + && !property.GetAttributes().Any(propertyAttribute => + propertyAttribute.AttributeClass!.Name is "SettingsViewModelExclusionAttribute")) + .Cast()) + { + namespaces.UseNamespace(usedTypes, typeSymbol, property.Type); + foreach (var propertyAttribute in property.GetAttributes()) { - namespaces.UseNamespace(usedTypes, typeSymbol, property.Type); - foreach (var propertyAttribute in property.GetAttributes()) + namespaces.UseNamespace(usedTypes, typeSymbol, propertyAttribute.AttributeClass!); + foreach (var attrConstructorArgument in propertyAttribute.ConstructorArguments.Where(arg => + arg.Value is INamedTypeSymbol)) { - namespaces.UseNamespace(usedTypes, typeSymbol, propertyAttribute.AttributeClass!); - foreach (var attrConstructorArgument in propertyAttribute.ConstructorArguments.Where(arg => arg.Value is INamedTypeSymbol)) - { - namespaces.UseNamespace(usedTypes, typeSymbol, (ITypeSymbol) attrConstructorArgument.Value!); - } + namespaces.UseNamespace(usedTypes, typeSymbol, (ITypeSymbol)attrConstructorArgument.Value!); } + } - propertySentences.Add(Spacing(1) + Regex.Replace(property.DeclaringSyntaxReferences[0].GetSyntax().ToString(), @"{[\s\S]+}", - $@" + propertySentences.Add(Spacing(1) + Regex.Replace( + property.DeclaringSyntaxReferences[0].GetSyntax().ToString(), @"{[\s\S]+}", + $@" {{ get => {settingName}.{property.Name}; set => SetProperty({settingName}.{property.Name}, value, {settingName}, (setting, value) => setting.{property.Name} = value); }}")); - } - - - var namespaceNames = namespaces.Skip(1).Aggregate("", (current, ns) => current + $"using {ns};\n"); - var allPropertySentences = propertySentences.Aggregate("\n", (current, ps) => current + $"{ps}\n\n"); - allPropertySentences = allPropertySentences.Substring(0, allPropertySentences.Length - 1); - var compilationUnit = nullable + namespaceNames + classBegin + allPropertySentences + classEnd; - return compilationUnit; } - return null; + + var namespaceNames = namespaces.Skip(1).Aggregate("", (current, ns) => current + $"using {ns};\n"); + var allPropertySentences = propertySentences.Aggregate("\n", (current, ps) => current + $"{ps}\n\n"); + allPropertySentences = allPropertySentences.Substring(0, allPropertySentences.Length - 1); + var compilationUnit = nullable + namespaceNames + classBegin + allPropertySentences + classEnd; + return compilationUnit; } } \ No newline at end of file diff --git a/src/Pixeval.SourceGen/TypeWithAttributeGenerator.cs b/src/Pixeval.SourceGen/TypeWithAttributeGenerator.cs index 51f76e050..4e526cc18 100644 --- a/src/Pixeval.SourceGen/TypeWithAttributeGenerator.cs +++ b/src/Pixeval.SourceGen/TypeWithAttributeGenerator.cs @@ -1,4 +1,25 @@ -using System; +#region Copyright (c) Pixeval/Pixeval.SourceGen + +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2021 Pixeval.SourceGen/LoadSaveConfigurationGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; @@ -20,9 +41,9 @@ public class TypeWithAttributeGenerator : IIncrementalGenerator /// /// /// - /// 判断是否是指定的attribute的比较器 + /// 该类的某种Attribute /// 生成的代码 - private delegate string? TypeWithAttribute(TypeDeclarationSyntax typeDeclarationSyntax, INamedTypeSymbol typeSymbol, Func attributeEqualityComparer); + private delegate string? TypeWithAttribute(TypeDeclarationSyntax typeDeclarationSyntax, INamedTypeSymbol typeSymbol, List attributeList); /// /// 需要生成的Attribute diff --git a/src/Pixeval.SourceGen/Utils.cs b/src/Pixeval.SourceGen/Utils.cs index 262940e7a..229add7c6 100644 --- a/src/Pixeval.SourceGen/Utils.cs +++ b/src/Pixeval.SourceGen/Utils.cs @@ -1,4 +1,26 @@ -using Microsoft.CodeAnalysis; +#region Copyright (c) Pixeval/Pixeval.SourceGen + +// GPL v3 License +// +// Pixeval/Pixeval.SourceGen +// Copyright (c) 2021 Pixeval.SourceGen/LoadSaveConfigurationGenerator.cs +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using System;