初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,252 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace PCL.Core.SourceGenerators;
|
||||
|
||||
public static class SharedExtensions
|
||||
{
|
||||
public static string ToLiteral(this string str) => SymbolDisplay.FormatLiteral(str, true);
|
||||
|
||||
public static string? ToPrimitive(this object? obj) => SymbolDisplay.FormatPrimitive(obj, true, false);
|
||||
|
||||
public static int GetDeclarationOrder(this ISymbol symbol)
|
||||
{
|
||||
var loc = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax().GetLocation();
|
||||
return loc?.SourceSpan.Start ?? int.MaxValue;
|
||||
}
|
||||
|
||||
extension(INamedTypeSymbol type)
|
||||
{
|
||||
public bool IsPartial()
|
||||
{
|
||||
foreach (var decl in type.DeclaringSyntaxReferences)
|
||||
{
|
||||
if (decl.GetSyntax() is ClassDeclarationSyntax { Modifiers: { } modifiers } &&
|
||||
modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsNestedWithin(INamedTypeSymbol potentialContainer)
|
||||
{
|
||||
var t = type.ContainingType;
|
||||
while (t is not null)
|
||||
{
|
||||
if (SymbolEqualityComparer.Default.Equals(t, potentialContainer))
|
||||
return true;
|
||||
t = t.ContainingType;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsAttribute()
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
while (baseType is not null)
|
||||
{
|
||||
if (baseType.ToDisplayString() == "System.Attribute") return true;
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GenerateTypeHeader(StringBuilder sb)
|
||||
{
|
||||
var ctnTypes = new Stack<INamedTypeSymbol>();
|
||||
for (var ctnType = type.ContainingType; ctnType is not null; ctnType = ctnType.ContainingType) ctnTypes.Push(ctnType);
|
||||
// namespace
|
||||
var ns = type.ContainingNamespace?.ToDisplayString();
|
||||
var indent = 0;
|
||||
if (!string.IsNullOrEmpty(ns))
|
||||
{
|
||||
sb.Append("namespace ").Append(ns).AppendLine();
|
||||
sb.AppendLine("{");
|
||||
indent++;
|
||||
}
|
||||
// outer classes
|
||||
foreach (var containingType in ctnTypes)
|
||||
{
|
||||
sb.Append(' ', indent * 4).Append("partial class ").Append(containingType.Name).AppendLine();
|
||||
sb.Append(' ', indent * 4).AppendLine("{");
|
||||
indent++;
|
||||
}
|
||||
// class
|
||||
sb.Append(' ', indent * 4).Append("partial class ").Append(type.Name).AppendLine();
|
||||
sb.Append(' ', indent * 4).AppendLine("{");
|
||||
return indent + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static string RenderDefaultValueCode(this SemanticModel sm, ExpressionSyntax expr)
|
||||
{
|
||||
if (expr is LiteralExpressionSyntax || expr.IsNegativeNumeric())
|
||||
return expr.ToString();
|
||||
|
||||
if (expr is TypeOfExpressionSyntax toe)
|
||||
{
|
||||
var type = sm.GetTypeInfo(toe.Type).Type;
|
||||
if (type is not null)
|
||||
return "typeof(" + type.GetFullyQualifiedName() + ")";
|
||||
return expr.ToString();
|
||||
}
|
||||
|
||||
if (expr is InvocationExpressionSyntax
|
||||
{
|
||||
Expression: IdentifierNameSyntax { Identifier.ValueText: "nameof" },
|
||||
ArgumentList.Arguments.Count: 1
|
||||
} inv)
|
||||
{
|
||||
var targetExpr = inv.ArgumentList.Arguments[0].Expression;
|
||||
var sym = sm.GetSymbolInfo(targetExpr).Symbol;
|
||||
if (sym is not null)
|
||||
{
|
||||
return "nameof(" + sym.GetQualifiedSymbolName() + ")";
|
||||
}
|
||||
return expr.ToString();
|
||||
}
|
||||
|
||||
var s = sm.GetSymbolInfo(expr).Symbol;
|
||||
if (s is IFieldSymbol fs)
|
||||
{
|
||||
return fs.GetQualifiedSymbolName();
|
||||
}
|
||||
|
||||
return expr.ToString();
|
||||
}
|
||||
|
||||
public static bool IsNegativeNumeric(this ExpressionSyntax expr)
|
||||
{
|
||||
return expr is PrefixUnaryExpressionSyntax p
|
||||
&& p.IsKind(SyntaxKind.UnaryMinusExpression)
|
||||
&& p.Operand is LiteralExpressionSyntax l
|
||||
&& l.IsKind(SyntaxKind.NumericLiteralExpression);
|
||||
}
|
||||
|
||||
extension(ISymbol symbol)
|
||||
{
|
||||
public string GetQualifiedSymbolName()
|
||||
{
|
||||
if (symbol is ITypeSymbol ts) return ts.GetFullyQualifiedName();
|
||||
|
||||
var parts = new Stack<string>();
|
||||
parts.Push(symbol.Name);
|
||||
var t = symbol.ContainingType;
|
||||
while (t is not null)
|
||||
{
|
||||
parts.Push(t.Name);
|
||||
t = t.ContainingType;
|
||||
}
|
||||
var ns = symbol.ContainingNamespace?.ToDisplayString();
|
||||
if (!string.IsNullOrEmpty(ns)) parts.Push(ns!);
|
||||
return string.Join(".", parts);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly SymbolDisplayFormat _SimplifiedTypeNameFormat = new(
|
||||
globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.OmittedAsContaining,
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
|
||||
miscellaneousOptions:
|
||||
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
|
||||
SymbolDisplayMiscellaneousOptions.CollapseTupleTypes |
|
||||
SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier |
|
||||
SymbolDisplayMiscellaneousOptions.UseSpecialTypes,
|
||||
genericsOptions: SymbolDisplayGenericsOptions.None
|
||||
);
|
||||
|
||||
private static readonly SymbolDisplayFormat _FullQualifiedNameFormat = new(
|
||||
typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
|
||||
genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
|
||||
miscellaneousOptions:
|
||||
SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers |
|
||||
SymbolDisplayMiscellaneousOptions.UseSpecialTypes
|
||||
);
|
||||
|
||||
extension(ITypeSymbol type)
|
||||
{
|
||||
public string GetSimplifiedTypeName()
|
||||
{
|
||||
return type.ToDisplayString(_SimplifiedTypeNameFormat);
|
||||
}
|
||||
|
||||
public string GetFullyQualifiedName()
|
||||
{
|
||||
if (type is INamedTypeSymbol {
|
||||
OriginalDefinition.SpecialType: SpecialType.System_Nullable_T,
|
||||
TypeArguments.Length: 1 } nt)
|
||||
{
|
||||
var inner = nt.TypeArguments[0];
|
||||
return inner.GetFullyQualifiedName() + "?";
|
||||
}
|
||||
if (type.TryGetSpecialTypeKeyword(out var keyword)) return keyword;
|
||||
return type.ToDisplayString(_FullQualifiedNameFormat);
|
||||
}
|
||||
|
||||
public bool TryGetSpecialTypeKeyword(out string keyword)
|
||||
{
|
||||
switch (type.SpecialType)
|
||||
{
|
||||
case SpecialType.System_Boolean: keyword = "bool"; return true;
|
||||
case SpecialType.System_Byte: keyword = "byte"; return true;
|
||||
case SpecialType.System_SByte: keyword = "sbyte"; return true;
|
||||
case SpecialType.System_Int16: keyword = "short"; return true;
|
||||
case SpecialType.System_UInt16: keyword = "ushort"; return true;
|
||||
case SpecialType.System_Int32: keyword = "int"; return true;
|
||||
case SpecialType.System_UInt32: keyword = "uint"; return true;
|
||||
case SpecialType.System_Int64: keyword = "long"; return true;
|
||||
case SpecialType.System_UInt64: keyword = "ulong"; return true;
|
||||
case SpecialType.System_IntPtr: keyword = "nint"; return true;
|
||||
case SpecialType.System_UIntPtr: keyword = "nuint"; return true;
|
||||
case SpecialType.System_Char: keyword = "char"; return true;
|
||||
case SpecialType.System_String: keyword = "string"; return true;
|
||||
case SpecialType.System_Object: keyword = "object"; return true;
|
||||
case SpecialType.System_Single: keyword = "float"; return true;
|
||||
case SpecialType.System_Double: keyword = "double"; return true;
|
||||
case SpecialType.System_Decimal: keyword = "decimal"; return true;
|
||||
default: keyword = ""; return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetQualifiedPropertyAccess(this IPropertySymbol prop)
|
||||
{
|
||||
var owner = prop.ContainingType.GetFullyQualifiedName();
|
||||
return owner + "." + prop.Name;
|
||||
}
|
||||
|
||||
public static bool IsAwaitable(this IMethodSymbol method)
|
||||
{
|
||||
// TODO this is a very naive implementation.
|
||||
return method.ReturnType.GetSimplifiedTypeName() == "System.Threading.Tasks.Task";
|
||||
}
|
||||
|
||||
public static string CorrectConfigTypeName(this string typeName, out string? fullTypeName)
|
||||
{
|
||||
var isArgConfig = typeName.StartsWith("PCL.Core.App.Configuration.ArgConfig<");
|
||||
if (isArgConfig)
|
||||
{
|
||||
fullTypeName = typeName;
|
||||
typeName = typeName.Substring(37, typeName.Length - 38);
|
||||
}
|
||||
else fullTypeName = null;
|
||||
return typeName;
|
||||
}
|
||||
|
||||
extension(string str)
|
||||
{
|
||||
public string SnakeIdToPascal()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
foreach (var part in str.Split('-'))
|
||||
{
|
||||
if (part.Length == 0) continue;
|
||||
sb.Append(char.ToUpper(part[0])).Append(part.Substring(1));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user