using System; using System.Collections.Immutable; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace Nebula.SourceGenerators; [Generator] public class ViewConstructorGenerator : IIncrementalGenerator { private static readonly string ViewModelAttribute = "Nebula.Shared.ViewHelper.ViewModelRegisterAttribute"; public void Initialize(IncrementalGeneratorInitializationContext context) { var provider = context.SyntaxProvider .CreateSyntaxProvider( (s, _) => s is ClassDeclarationSyntax, (ctx, _) => SourceHelper.GetClassDeclarationForSourceGen(ctx,ViewModelAttribute)) .Where(t => t.reportAttributeFound) .Select((t, _) => t.Item1); context.RegisterSourceOutput(context.CompilationProvider.Combine(provider.Collect()), (ctx, t) => GenerateCode(ctx, t.Left, t.Right)); } private void GenerateCode(SourceProductionContext context, Compilation compilation, ImmutableArray classDeclarations) { foreach (var classDeclarationSyntax in classDeclarations) { var semanticModel = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree); if (semanticModel.GetDeclaredSymbol(classDeclarationSyntax) is not INamedTypeSymbol classSymbol) continue; var viewModelName = classSymbol.Name; var viewModelNamespace = classSymbol.ContainingNamespace.ToDisplayString(); var viewModelRegisterAttr = classSymbol.GetAttributes() .FirstOrDefault(attr => attr.AttributeClass?.ToDisplayString() == ViewModelAttribute); var viewTypeArg = viewModelRegisterAttr?.ConstructorArguments.FirstOrDefault(); if (viewTypeArg?.Value is not INamedTypeSymbol viewTypeSymbol) continue; try { var viewName = viewTypeSymbol.Name; var viewNamespace = viewTypeSymbol.ContainingNamespace.ToDisplayString(); var code = $@" namespace {viewNamespace} {{ public partial class {viewName} {{ public {viewName}({viewModelNamespace}.{viewModelName} viewModel) : this() {{ DataContext = viewModel; }} }} }}"; // Add the source code to the compilation. context.AddSource($"{viewName}_viewConstructAuto.g.cs", SourceText.From(code, Encoding.UTF8)); } catch (Exception e) { var coder1 = $@" // // Error! namespace {viewModelNamespace} {{ public partial class {viewModelName} {{ // {e.Message} }} }}"; // Add the source code to the compilation. context.AddSource($"{viewModelName}_viewError.g.cs", SourceText.From(coder1, Encoding.UTF8)); } } } }