|
|
|
@ -1,11 +1,11 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Collections.Immutable; |
|
|
|
using System.Linq; |
|
|
|
using Avalonia.Generators.Common; |
|
|
|
using Avalonia.Generators.Common.Domain; |
|
|
|
using Avalonia.Generators.Compiler; |
|
|
|
using Microsoft.CodeAnalysis; |
|
|
|
using Microsoft.CodeAnalysis.CSharp; |
|
|
|
|
|
|
|
namespace Avalonia.Generators.NameGenerator; |
|
|
|
|
|
|
|
@ -13,6 +13,7 @@ namespace Avalonia.Generators.NameGenerator; |
|
|
|
public class AvaloniaNameIncrementalGenerator : IIncrementalGenerator |
|
|
|
{ |
|
|
|
private const string SourceItemGroupMetadata = "build_metadata.AdditionalFiles.SourceItemGroup"; |
|
|
|
private static readonly MiniCompiler s_noopCompiler = MiniCompiler.CreateNoop(); |
|
|
|
|
|
|
|
public void Initialize(IncrementalGeneratorInitializationContext context) |
|
|
|
{ |
|
|
|
@ -53,24 +54,12 @@ public class AvaloniaNameIncrementalGenerator : IIncrementalGenerator |
|
|
|
.Select(static (pair, _) => pair.Left) |
|
|
|
.WithTrackingName(TrackingNames.InputXamlFilesProvider); |
|
|
|
|
|
|
|
// Map compilation into readonly XAML type system.
|
|
|
|
// Which is ONLY updated when any compilation references have changed.
|
|
|
|
var typeSystem = context.CompilationProvider |
|
|
|
.WithComparer(new CompilationReferencesComparer()) |
|
|
|
.Select(static (compilation, _) => new RoslynTypeSystem((CSharpCompilation)compilation)) |
|
|
|
.WithTrackingName(TrackingNames.XamlTypeSystemProvider); |
|
|
|
|
|
|
|
// Actual parsing step. We input XAML files one by one, and reuse readonly type system.
|
|
|
|
// It's detached from the up-to-date compilation info, we can't access type information here yet.
|
|
|
|
// Otherwise slow parsing would slow down IDE on any file edited.
|
|
|
|
// This pipeline step only depends on the input xaml tiles and type system (which depends on assembly references, but not actual types).
|
|
|
|
var partialFilesInfo = xamlFiles.Combine(typeSystem) |
|
|
|
.Select(static (pair, cancellationToken) => |
|
|
|
// Actual parsing step. We input XAML files one by one, but don't resolve any types.
|
|
|
|
// That's why we use NoOp type system here, allowing parsing to run detached from C# compilation.
|
|
|
|
// Otherwise we would need to re-parse XAML on any C# file changed.
|
|
|
|
var parsedXamlClasses = xamlFiles |
|
|
|
.Select(static (file, cancellationToken) => |
|
|
|
{ |
|
|
|
var (file, types) = pair; |
|
|
|
|
|
|
|
var compiler = MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute); |
|
|
|
var canAttachDevTools = types.FindAssembly("Avalonia.Diagnostics") is not null; |
|
|
|
var text = file.GetText(cancellationToken); |
|
|
|
var diagnostics = new List<DiagnosticDescriptor>(); |
|
|
|
if (text is not null) |
|
|
|
@ -78,10 +67,7 @@ public class AvaloniaNameIncrementalGenerator : IIncrementalGenerator |
|
|
|
try |
|
|
|
{ |
|
|
|
var xaml = text.ToString(); |
|
|
|
var viewResolver = new XamlXViewResolver( |
|
|
|
types, compiler, true, |
|
|
|
invalidType => |
|
|
|
diagnostics.Add(GeneratorExtensions.NameGeneratorInvalidType(invalidType))); |
|
|
|
var viewResolver = new XamlXViewResolver(s_noopCompiler); |
|
|
|
var view = viewResolver.ResolveView(xaml); |
|
|
|
if (view is null) |
|
|
|
{ |
|
|
|
@ -89,26 +75,83 @@ public class AvaloniaNameIncrementalGenerator : IIncrementalGenerator |
|
|
|
} |
|
|
|
|
|
|
|
var nameResolver = new XamlXNameResolver(); |
|
|
|
var resolvedNames = nameResolver.ResolveNames(view.Xaml); |
|
|
|
var xmlNames = nameResolver.ResolveXmlNames(view.Xaml); |
|
|
|
|
|
|
|
return new PartialClassInfo( |
|
|
|
new ResolvedViewWithNames(view, resolvedNames), |
|
|
|
canAttachDevTools, |
|
|
|
return new XmlClassInfo( |
|
|
|
new ResolvedXmlView(view, xmlNames), |
|
|
|
diagnostics.ToImmutableArray()); |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
diagnostics.Add(GeneratorExtensions.NameGeneratorUnhandledError(ex)); |
|
|
|
return new PartialClassInfo(null, canAttachDevTools, diagnostics.ToImmutableArray()); |
|
|
|
return new XmlClassInfo(null, diagnostics.ToImmutableArray()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
}) |
|
|
|
.Where(request => request is not null) |
|
|
|
.WithTrackingName(TrackingNames.ParsedXamlPartialFiles); |
|
|
|
.WithTrackingName(TrackingNames.ParsedXamlClasses); |
|
|
|
|
|
|
|
// Note: this step will be re-executed on any C# file changes.
|
|
|
|
// As much as possible heavy tasks should be moved outside of this step, like XAML parsing.
|
|
|
|
var resolvedNames = parsedXamlClasses.Collect() |
|
|
|
.Combine(context.CompilationProvider) |
|
|
|
.SelectMany(static (pair, _) => |
|
|
|
{ |
|
|
|
var (classes, compilation) = pair; |
|
|
|
var roslynTypeSystem = new RoslynTypeSystem(compilation); |
|
|
|
var compiler = MiniCompiler.CreateRoslyn(roslynTypeSystem, MiniCompiler.AvaloniaXmlnsDefinitionAttribute); |
|
|
|
var hasDevToolsReference = compilation.ReferencedAssemblyNames.Any(r => r.Name == "Avalonia.Diagnostics"); |
|
|
|
var nameResolver = new XamlXNameResolver(); |
|
|
|
|
|
|
|
var outputs = new List<ResolvedClassInfo>(); |
|
|
|
foreach (var classInfo in classes) |
|
|
|
{ |
|
|
|
var diagnostics = new List<DiagnosticDescriptor>(classInfo!.Diagnostics); |
|
|
|
if (classInfo.XmlView is { } xmlView) |
|
|
|
{ |
|
|
|
var type = roslynTypeSystem.FindType(xmlView.FullName); |
|
|
|
ResolvedView? view = null; |
|
|
|
|
|
|
|
if (type is null) |
|
|
|
{ |
|
|
|
diagnostics.Add(GeneratorExtensions.NameGeneratorInvalidType(xmlView.FullName)); |
|
|
|
} |
|
|
|
else if (type.IsAvaloniaStyledElement()) |
|
|
|
{ |
|
|
|
var resolvedNames = new List<ResolvedName>(); |
|
|
|
foreach (var xmlName in xmlView.XmlNames) |
|
|
|
{ |
|
|
|
try |
|
|
|
{ |
|
|
|
var clrType = compiler.ResolveXamlType(xmlName.XmlType); |
|
|
|
if (!clrType.IsAvaloniaStyledElement()) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
resolvedNames.Add(nameResolver |
|
|
|
.ResolveName(clrType, xmlName.Name, xmlName.FieldModifier)); |
|
|
|
} |
|
|
|
catch (Exception ex) |
|
|
|
{ |
|
|
|
diagnostics.Add(GeneratorExtensions.NameGeneratorUnhandledError(ex)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
view = new ResolvedView(xmlView, type.IsAvaloniaWindow(), resolvedNames.ToImmutableArray()); |
|
|
|
} |
|
|
|
|
|
|
|
context.RegisterSourceOutput(partialFilesInfo.Combine(options), static (context, pair) => |
|
|
|
outputs.Add(new ResolvedClassInfo(view, hasDevToolsReference, diagnostics.ToImmutableArray())); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return outputs; |
|
|
|
}) |
|
|
|
.WithTrackingName(TrackingNames.ResolvedNamesProvider); |
|
|
|
|
|
|
|
context.RegisterSourceOutput(resolvedNames.Combine(options), static (context, pair) => |
|
|
|
{ |
|
|
|
var (info, options) = pair; |
|
|
|
|
|
|
|
@ -117,7 +160,7 @@ public class AvaloniaNameIncrementalGenerator : IIncrementalGenerator |
|
|
|
context.Report(diagnostic); |
|
|
|
} |
|
|
|
|
|
|
|
if (info.ViewInfo is { } view && options.AvaloniaNameGeneratorFilterByNamespace.Matches(view.Namespace)) |
|
|
|
if (info.View is { } view && options.AvaloniaNameGeneratorFilterByNamespace.Matches(view.Namespace)) |
|
|
|
{ |
|
|
|
ICodeGenerator codeGenerator = options.AvaloniaNameGeneratorBehavior switch |
|
|
|
{ |
|
|
|
@ -138,17 +181,21 @@ public class AvaloniaNameIncrementalGenerator : IIncrementalGenerator |
|
|
|
}; |
|
|
|
|
|
|
|
var generatedPartialClass = codeGenerator.GenerateCode( |
|
|
|
info.ViewInfo.ClassName, |
|
|
|
info.ViewInfo.Namespace, |
|
|
|
info.ViewInfo.ResolvedNames); |
|
|
|
info.View.ClassName, |
|
|
|
info.View.Namespace, |
|
|
|
info.View.Names); |
|
|
|
|
|
|
|
context.AddSource(fileName, generatedPartialClass); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal record PartialClassInfo( |
|
|
|
ResolvedViewWithNames? ViewInfo, |
|
|
|
bool CanAttachDevTools, |
|
|
|
ImmutableArray<DiagnosticDescriptor> Diagnostics); |
|
|
|
internal record XmlClassInfo( |
|
|
|
ResolvedXmlView? XmlView, |
|
|
|
ImmutableArray<DiagnosticDescriptor> Diagnostics); |
|
|
|
|
|
|
|
internal record ResolvedClassInfo( |
|
|
|
ResolvedView? View, |
|
|
|
bool CanAttachDevTools, |
|
|
|
ImmutableArray<DiagnosticDescriptor> Diagnostics); |
|
|
|
} |
|
|
|
|