瀏覽代碼

Migrate AvaloniaNameSourceGenerator to IIncrementalGenerator (#19216)

* Add AnalyzerProject.targets targets

* PrivateAssets on Workspaces.Common

* Migrate AvaloniaNameSourceGenerator to IIncrementalGenerator

* Remove outdated lins in the generator readme

* Add GlobPattern.ToString

* Fix tests

* Formatting

* Redo pipeline, make steps more independent from each other, compilation should be reused between dependency changes

* Split XAML parsing and type resolution into separated steps

* Restore CompilationReferencesComparer usage

* Revert "Restore CompilationReferencesComparer usage"

This reverts commit c51341990b8030bb2594ee66e8819b903383bcc1.

* Split ResolvedNamesProvider pipeline step, process files in parallel

* Add comment

* Switch back to EquatableList

* Add cancellation to the incremenetal source gen

* Rethrow cancellation exception
Max Katz 2 月之前
父節點
當前提交
fca45c1c16
共有 38 個文件被更改,包括 708 次插入484 次删除
  1. 1 0
      Avalonia.sln
  2. 14 0
      build/AnalyzerProject.targets
  3. 1 1
      samples/Generators.Sandbox/Controls/SignUpView.xaml
  4. 3 10
      src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj
  5. 1 7
      src/tools/Avalonia.Generators/Avalonia.Generators.csproj
  6. 1 2
      src/tools/Avalonia.Generators/Common/Domain/ICodeGenerator.cs
  7. 3 1
      src/tools/Avalonia.Generators/Common/Domain/IGlobPattern.cs
  8. 9 3
      src/tools/Avalonia.Generators/Common/Domain/INameResolver.cs
  9. 38 3
      src/tools/Avalonia.Generators/Common/Domain/IViewResolver.cs
  10. 58 0
      src/tools/Avalonia.Generators/Common/EquatableList.cs
  11. 7 0
      src/tools/Avalonia.Generators/Common/GlobPattern.cs
  12. 14 8
      src/tools/Avalonia.Generators/Common/GlobPatternGroup.cs
  13. 7 13
      src/tools/Avalonia.Generators/Common/ResolverExtensions.cs
  14. 40 25
      src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs
  15. 22 53
      src/tools/Avalonia.Generators/Common/XamlXViewResolver.cs
  16. 61 3
      src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs
  17. 22 0
      src/tools/Avalonia.Generators/Compiler/NoopTypeSystem.cs
  18. 5 3
      src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs
  19. 0 44
      src/tools/Avalonia.Generators/GeneratorContextExtensions.cs
  20. 43 0
      src/tools/Avalonia.Generators/GeneratorExtensions.cs
  21. 49 34
      src/tools/Avalonia.Generators/GeneratorOptions.cs
  22. 0 66
      src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameGenerator.cs
  23. 211 0
      src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameIncrementalGenerator.cs
  24. 0 86
      src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs
  25. 3 9
      src/tools/Avalonia.Generators/NameGenerator/INameGenerator.cs
  26. 5 24
      src/tools/Avalonia.Generators/NameGenerator/InitializeComponentCodeGenerator.cs
  27. 3 3
      src/tools/Avalonia.Generators/NameGenerator/OnlyPropertiesCodeGenerator.cs
  28. 10 0
      src/tools/Avalonia.Generators/NameGenerator/TrackingNames.cs
  29. 1 20
      src/tools/Avalonia.Generators/README.md
  30. 1 9
      src/tools/DevAnalyzers/DevAnalyzers.csproj
  31. 1 7
      src/tools/DevGenerators/DevGenerators.csproj
  32. 1 1
      tests/Avalonia.Generators.Tests/Avalonia.Generators.Tests.csproj
  33. 24 0
      tests/Avalonia.Generators.Tests/CompilationUtils.cs
  34. 16 14
      tests/Avalonia.Generators.Tests/InitializeComponent/InitializeComponentTests.cs
  35. 2 2
      tests/Avalonia.Generators.Tests/MiniCompilerTests.cs
  36. 15 12
      tests/Avalonia.Generators.Tests/OnlyProperties/OnlyPropertiesTests.cs
  37. 3 10
      tests/Avalonia.Generators.Tests/XamlXClassResolverTests.cs
  38. 13 11
      tests/Avalonia.Generators.Tests/XamlXNameResolverTests.cs

+ 1 - 0
Avalonia.sln

@@ -121,6 +121,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
 		build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
 		build\WarnAsErrors.props = build\WarnAsErrors.props
 		build\XUnit.props = build\XUnit.props
+		build\AnalyzerProject.targets = build\AnalyzerProject.targets
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{4D6FAF79-58B4-482F-9122-0668C346364C}"

+ 14 - 0
build/AnalyzerProject.targets

@@ -0,0 +1,14 @@
+<Project>
+
+  <PropertyGroup>
+    <EnforceExtendedAnalyzerRules Condition="'$(EnforceExtendedAnalyzerRules)' == ''">true</EnforceExtendedAnalyzerRules>
+    <IsRoslynComponent Condition="'$(IsRoslynComponent)' == ''">true</IsRoslynComponent>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all"/>
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.5.0" PrivateAssets="all" />
+  </ItemGroup>
+
+</Project>

+ 1 - 1
samples/Generators.Sandbox/Controls/SignUpView.xaml

@@ -8,7 +8,7 @@
                  Watermark="Please, enter user name..."
                  UseFloatingWatermark="True" />
         <TextBlock x:Name="UserNameValidation" 
-                   Foreground="Red"
+                   Foreground="Green"
                    FontSize="12" />
         <TextBox Margin="0 10 0 0"
                  x:Name="PasswordTextBox"

+ 3 - 10
src/tools/Avalonia.Analyzers/Avalonia.Analyzers.csproj

@@ -2,23 +2,16 @@
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
     <IncludeBuildOutput>false</IncludeBuildOutput>
-    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
     <DebugType>embedded</DebugType>
     <IsPackable>true</IsPackable>
     <IncludeSymbols>false</IncludeSymbols>
-    <IsRoslynComponent>true</IsRoslynComponent>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all"/>
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
-    <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="3.9.0" />
-  </ItemGroup>
-
   <ItemGroup>
     <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
   </ItemGroup>
 
-  <Import Project="..\..\..\build\TrimmingEnable.props" />
-  <Import Project="..\..\..\build\NullableEnable.props" />
+  <Import Project="../../../build/TrimmingEnable.props" />
+  <Import Project="../../../build/NullableEnable.props" />
+  <Import Project="../../../build/AnalyzerProject.targets" />
 </Project>

+ 1 - 7
src/tools/Avalonia.Generators/Avalonia.Generators.csproj

@@ -6,17 +6,10 @@
     <DebugType>embedded</DebugType>
     <IsPackable>true</IsPackable>
     <IncludeSymbols>false</IncludeSymbols>
-    <IsRoslynComponent>true</IsRoslynComponent>
-    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
     <Nullable>enable</Nullable>
     <XamlXSourcePath>../../../external/XamlX/src/XamlX</XamlXSourcePath>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
-    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
-  </ItemGroup>
-
   <ItemGroup>
     <Compile Include="$(XamlXSourcePath)/**/*.cs"
              Exclude="$(XamlXSourcePath)/obj/**/*.cs;$(XamlXSourcePath)/IL/SreTypeSystem.cs"
@@ -35,4 +28,5 @@
   </ItemGroup>
 
   <Import Project="../../../build/TrimmingEnable.props" />
+  <Import Project="../../../build/AnalyzerProject.targets" />
 </Project>

+ 1 - 2
src/tools/Avalonia.Generators/Common/Domain/ICodeGenerator.cs

@@ -1,9 +1,8 @@
 using System.Collections.Generic;
-using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.Common.Domain;
 
 internal interface ICodeGenerator
 {
-    string GenerateCode(string className, string nameSpace, IXamlType xamlType, IEnumerable<ResolvedName> names);
+    string GenerateCode(string className, string nameSpace, IEnumerable<ResolvedName> names);
 }

+ 3 - 1
src/tools/Avalonia.Generators/Common/Domain/IGlobPattern.cs

@@ -1,6 +1,8 @@
+using System;
+
 namespace Avalonia.Generators.Common.Domain;
 
-internal interface IGlobPattern
+internal interface IGlobPattern : IEquatable<IGlobPattern>
 {
     bool Matches(string str);
 }

+ 9 - 3
src/tools/Avalonia.Generators/Common/Domain/INameResolver.cs

@@ -1,5 +1,7 @@
-using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Threading;
 using XamlX.Ast;
+using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.Common.Domain;
 
@@ -13,7 +15,11 @@ internal enum NamedFieldModifier
 
 internal interface INameResolver
 {
-    IReadOnlyList<ResolvedName> ResolveNames(XamlDocument xaml);
+    EquatableList<ResolvedXmlName> ResolveXmlNames(XamlDocument xaml, CancellationToken cancellationToken);
+    ResolvedName ResolveName(IXamlType xamlType, string name, string? fieldModifier);
 }
 
-internal record ResolvedName(string TypeName, string Name, string FieldModifier);
+internal record XamlXmlType(string Name, string? XmlNamespace, EquatableList<XamlXmlType> GenericArguments);
+
+internal record ResolvedXmlName(XamlXmlType XmlType, string Name, string? FieldModifier);
+internal record ResolvedName(string TypeName, string Name, string? FieldModifier);

+ 38 - 3
src/tools/Avalonia.Generators/Common/Domain/IViewResolver.cs

@@ -1,11 +1,46 @@
+using System.Collections.Immutable;
+using System.Threading;
 using XamlX.Ast;
-using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.Common.Domain;
 
 internal interface IViewResolver
 {
-    ResolvedView? ResolveView(string xaml);
+    ResolvedViewDocument? ResolveView(string xaml, CancellationToken cancellationToken);
 }
 
-internal record ResolvedView(string ClassName, IXamlType XamlType, string Namespace, XamlDocument Xaml);
+internal record ResolvedViewInfo(string ClassName, string Namespace)
+{
+    public string FullName => $"{Namespace}.{ClassName}";
+    public override string ToString() => FullName;
+}
+
+internal record ResolvedViewDocument(string ClassName, string Namespace, XamlDocument Xaml)
+    : ResolvedViewInfo(ClassName, Namespace);
+
+internal record ResolvedXmlView(
+    string ClassName,
+    string Namespace,
+    EquatableList<ResolvedXmlName> XmlNames)
+    : ResolvedViewInfo(ClassName, Namespace)
+{
+    public ResolvedXmlView(ResolvedViewInfo info, EquatableList<ResolvedXmlName> xmlNames)
+        : this(info.ClassName, info.Namespace, xmlNames)
+    {
+        
+    }
+}
+
+internal record ResolvedView(
+    string ClassName,
+    string Namespace,
+    bool IsWindow,
+    EquatableList<ResolvedName> Names)
+    : ResolvedViewInfo(ClassName, Namespace)
+{
+    public ResolvedView(ResolvedViewInfo info, bool isWindow, EquatableList<ResolvedName> names)
+        : this(info.ClassName, info.Namespace, isWindow, names)
+    {
+        
+    }
+}

+ 58 - 0
src/tools/Avalonia.Generators/Common/EquatableList.cs

@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace Avalonia.Generators.Common;
+
+// https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.cookbook.md#pipeline-model-design
+// With minor modification to use ReadOnlyCollection instead of List
+internal class EquatableList<T>(IList<T> collection)
+    : ReadOnlyCollection<T>(collection), IEquatable<EquatableList<T>>
+{
+    public bool Equals(EquatableList<T>? other)
+    {
+        // If the other list is null or a different size, they're not equal
+        if (other is null || Count != other.Count)
+        {
+            return false;
+        }
+
+        // Compare each pair of elements for equality
+        for (int i = 0; i < Count; i++)
+        {
+            if (!EqualityComparer<T>.Default.Equals(this[i], other[i]))
+            {
+                return false;
+            }
+        }
+
+        // If we got this far, the lists are equal
+        return true;
+    }
+
+    public override bool Equals(object? obj)
+    {
+        return Equals(obj as EquatableList<T>);
+    }
+
+    public override int GetHashCode()
+    {
+        var hash = 0;
+        for (var i = 0; i < Count; i++)
+        {
+            hash ^= this[i]?.GetHashCode() ?? 0;
+        }
+        return hash;
+    }
+
+    public static bool operator ==(EquatableList<T>? list1, EquatableList<T>? list2)
+    {
+        return ReferenceEquals(list1, list2)
+               || list1 is not null && list2 is not null && list1.Equals(list2);
+    }
+
+    public static bool operator !=(EquatableList<T>? list1, EquatableList<T>? list2)
+    {
+        return !(list1 == list2);
+    }
+}

+ 7 - 0
src/tools/Avalonia.Generators/Common/GlobPattern.cs

@@ -7,12 +7,19 @@ internal class GlobPattern : IGlobPattern
 {
     private const RegexOptions GlobOptions = RegexOptions.IgnoreCase | RegexOptions.Singleline;
     private readonly Regex _regex;
+    private readonly string _pattern;
 
     public GlobPattern(string pattern)
     {
+        _pattern = pattern;
         var expression = "^" + Regex.Escape(pattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$";
         _regex = new Regex(expression, GlobOptions);
     }
 
     public bool Matches(string str) => _regex.IsMatch(str);
+
+    public bool Equals(IGlobPattern other) => other is GlobPattern pattern && pattern._pattern == _pattern;
+    public override int GetHashCode() => _pattern.GetHashCode();
+    public override bool Equals(object? obj) => obj is GlobPattern pattern && Equals(pattern);
+    public override string ToString() => _pattern;
 }

+ 14 - 8
src/tools/Avalonia.Generators/Common/GlobPatternGroup.cs

@@ -4,14 +4,20 @@ using Avalonia.Generators.Common.Domain;
 
 namespace Avalonia.Generators.Common;
 
-internal class GlobPatternGroup : IGlobPattern
+internal class GlobPatternGroup(IEnumerable<string> patterns)
+    : EquatableList<GlobPattern>(patterns.Select(p => new GlobPattern(p)).ToArray()), IGlobPattern
 {
-    private readonly GlobPattern[] _patterns;
+    public bool Matches(string str)
+    {
+        for (var i = 0; i < Count; i++)
+        {
+            if (this[i].Matches(str))
+                return true;
+        }
+        return false;
+    }
 
-    public GlobPatternGroup(IEnumerable<string> patterns) =>
-        _patterns = patterns
-            .Select(pattern => new GlobPattern(pattern))
-            .ToArray();
-
-    public bool Matches(string str) => _patterns.Any(pattern => pattern.Matches(str));
+    public bool Equals(IGlobPattern other) => other is GlobPatternGroup group && base.Equals(group);
+    public override string ToString() => $"[{string.Join(", ", this.Select(p => p.ToString()))}]";
 }
+

+ 7 - 13
src/tools/Avalonia.Generators/Common/ResolverExtensions.cs

@@ -1,4 +1,4 @@
-using System.Linq;
+using System;
 using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.Common;
@@ -6,20 +6,14 @@ namespace Avalonia.Generators.Common;
 internal static class ResolverExtensions
 {
     public static bool IsAvaloniaStyledElement(this IXamlType clrType) =>
-        clrType.HasStyledElementBaseType() ||
-        clrType.HasIStyledElementInterface();
+        Inherits(clrType, "Avalonia.StyledElement");
+    public static bool IsAvaloniaWindow(this IXamlType clrType) =>
+        Inherits(clrType, "Avalonia.Controls.Window");
 
-    private static bool HasStyledElementBaseType(this IXamlType clrType)
+    private static bool Inherits(IXamlType clrType, string metadataName)
     {
-        // Check for the base type since IStyledElement interface is removed.
-        // https://github.com/AvaloniaUI/Avalonia/pull/9553
-        if (clrType.FullName == "Avalonia.StyledElement")
+        if (string.Equals(clrType.FullName, metadataName, StringComparison.Ordinal))
             return true;
-        return clrType.BaseType != null && IsAvaloniaStyledElement(clrType.BaseType);
+        return clrType.BaseType is { } baseType && Inherits(baseType, metadataName);
     }
-
-    private static bool HasIStyledElementInterface(this IXamlType clrType) =>
-        clrType.Interfaces.Any(abstraction =>
-            abstraction.IsInterface &&
-            abstraction.FullName == "Avalonia.IStyledElement");
 }

+ 40 - 25
src/tools/Avalonia.Generators/Common/XamlXNameResolver.cs

@@ -1,38 +1,56 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Collections.Immutable;
 using System.Linq;
+using System.Threading;
 using Avalonia.Generators.Common.Domain;
 using XamlX;
 using XamlX.Ast;
+using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.Common;
 
-internal class XamlXNameResolver : INameResolver, IXamlAstVisitor
+internal class XamlXNameResolver
+    : INameResolver, IXamlAstVisitor
 {
-    private readonly List<ResolvedName> _items = new();
-    private readonly string _defaultFieldModifier;
+    private readonly Dictionary<string, ResolvedXmlName> _items = new();
+    private CancellationToken _cancellationToken;
 
-    public XamlXNameResolver(NamedFieldModifier namedFieldModifier = NamedFieldModifier.Internal)
+    public EquatableList<ResolvedXmlName> ResolveXmlNames(XamlDocument xaml, CancellationToken cancellationToken)
     {
-        _defaultFieldModifier = namedFieldModifier.ToString().ToLowerInvariant();
+        _items.Clear();
+        try
+        {
+            _cancellationToken = cancellationToken;
+            xaml.Root.Visit(this);
+            xaml.Root.VisitChildren(this);
+        }
+        finally
+        {
+            _cancellationToken = CancellationToken.None;
+        }
+
+        return new EquatableList<ResolvedXmlName>(_items.Values.ToArray());
     }
 
-    public IReadOnlyList<ResolvedName> ResolveNames(XamlDocument xaml)
+    public ResolvedName ResolveName(IXamlType clrType, string name, string? fieldModifier)
     {
-        _items.Clear();
-        xaml.Root.Visit(this);
-        xaml.Root.VisitChildren(this);
-        return _items;
+        var typeName = $"{clrType.Namespace}.{clrType.Name}";
+        var typeAgs = clrType.GenericArguments.Select(arg => arg.FullName).ToImmutableList();
+        var genericTypeName = typeAgs.Count == 0
+            ? $"global::{typeName}"
+            : $"global::{typeName}<{string.Join(", ", typeAgs.Select(arg => $"global::{arg}"))}>";
+        return new ResolvedName(genericTypeName, name, fieldModifier);
     }
 
     IXamlAstNode IXamlAstVisitor.Visit(IXamlAstNode node)
     {
+        _cancellationToken.ThrowIfCancellationRequested();
+
         if (node is not XamlAstObjectNode objectNode)
             return node;
 
-        var clrType = objectNode.Type.GetClrType();
-        if (!clrType.IsAvaloniaStyledElement())
-            return node;
+        var xamlType = (XamlAstXmlTypeReference)objectNode.Type;
 
         foreach (var child in objectNode.Children)
         {
@@ -44,27 +62,24 @@ internal class XamlXNameResolver : INameResolver, IXamlAstVisitor
                 propertyValueNode.Values[0] is XamlAstTextNode text)
             {
                 var fieldModifier = TryGetFieldModifier(objectNode);
-                var typeName = $@"{clrType.Namespace}.{clrType.Name}";
-                var typeAgs = clrType.GenericArguments.Select(arg => arg.FullName).ToImmutableList();
-                var genericTypeName = typeAgs.Count == 0
-                    ? $"global::{typeName}"
-                    : $@"global::{typeName}<{string.Join(", ", typeAgs.Select(arg => $"global::{arg}"))}>";
-
-                var resolvedName = new ResolvedName(genericTypeName, text.Text, fieldModifier);
-                if (_items.Contains(resolvedName))
+                var resolvedName = new ResolvedXmlName(ConvertType(xamlType), text.Text, fieldModifier);
+                if (_items.ContainsKey(text.Text))
                     continue;
-                _items.Add(resolvedName);
+                _items.Add(text.Text, resolvedName);
             }
         }
 
         return node;
+
+        static XamlXmlType ConvertType(XamlAstXmlTypeReference type) => new(type.Name, type.XmlNamespace,
+            new EquatableList<XamlXmlType>(type.GenericArguments.Select(ConvertType).ToArray()));
     }
 
     void IXamlAstVisitor.Push(IXamlAstNode node) { }
 
     void IXamlAstVisitor.Pop() { }
 
-    private string TryGetFieldModifier(XamlAstObjectNode objectNode)
+    private string? TryGetFieldModifier(XamlAstObjectNode objectNode)
     {
         // We follow Xamarin.Forms API behavior in terms of x:FieldModifier here:
         // https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/field-modifiers
@@ -87,7 +102,7 @@ internal class XamlXNameResolver : INameResolver, IXamlAstVisitor
             "protected" => "protected",
             "internal" => "internal",
             "notpublic" => "internal",
-            _ => _defaultFieldModifier
+            _ => null
         };
     }
 

+ 22 - 53
src/tools/Avalonia.Generators/Common/XamlXViewResolver.cs

@@ -1,92 +1,61 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Threading;
 using Avalonia.Generators.Common.Domain;
 using Avalonia.Generators.Compiler;
 using XamlX;
 using XamlX.Ast;
 using XamlX.Parsers;
+using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.Common;
 
-internal class XamlXViewResolver : IViewResolver, IXamlAstVisitor
+internal class XamlXViewResolver(MiniCompiler compiler) : IViewResolver, IXamlAstVisitor
 {
-    private readonly RoslynTypeSystem _typeSystem;
-    private readonly MiniCompiler _compiler;
-    private readonly bool _checkTypeValidity;
-    private readonly Action<string>? _onTypeInvalid;
-    private readonly Action<Exception>? _onUnhandledError;
-
-    private ResolvedView? _resolvedClass;
+    private ResolvedViewDocument? _resolvedClass;
     private XamlDocument? _xaml;
+    private CancellationToken _cancellationToken;
 
-    public XamlXViewResolver(
-        RoslynTypeSystem typeSystem,
-        MiniCompiler compiler,
-        bool checkTypeValidity = false,
-        Action<string>? onTypeInvalid = null,
-        Action<Exception>? onUnhandledError = null)
+    public ResolvedViewDocument? ResolveView(string xaml, CancellationToken cancellationToken)
     {
-        _checkTypeValidity = checkTypeValidity;
-        _onTypeInvalid = onTypeInvalid;
-        _onUnhandledError = onUnhandledError;
-        _typeSystem = typeSystem;
-        _compiler = compiler;
-    }
+        _resolvedClass = null;
+        _xaml = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string>
+        {
+            {XamlNamespaces.Blend2008, XamlNamespaces.Blend2008}
+        });
 
-    public ResolvedView? ResolveView(string xaml)
-    {
         try
         {
-            _resolvedClass = null;
-            _xaml = XDocumentXamlParser.Parse(xaml, new Dictionary<string, string>
-            {
-                {XamlNamespaces.Blend2008, XamlNamespaces.Blend2008}
-            });
-
-            _compiler.Transform(_xaml);
+            _cancellationToken = cancellationToken;
+            compiler.TransformWithCancellation(_xaml, cancellationToken);
             _xaml.Root.Visit(this);
             _xaml.Root.VisitChildren(this);
-            return _resolvedClass;
         }
-        catch (Exception exception)
+        finally
         {
-            _onUnhandledError?.Invoke(exception);
-            return null;
+            _cancellationToken = CancellationToken.None;
         }
+        return _resolvedClass;
     }
-    
+
     IXamlAstNode IXamlAstVisitor.Visit(IXamlAstNode node)
     {
-        if (node is not XamlAstObjectNode objectNode)
-            return node;
+        _cancellationToken.ThrowIfCancellationRequested();
 
-        var clrType = objectNode.Type.GetClrType();
-        if (!clrType.IsAvaloniaStyledElement())
+        if (node is not XamlAstObjectNode objectNode)
             return node;
 
         foreach (var child in objectNode.Children)
         {
-            if (child is XamlAstXmlDirective directive &&
-                directive.Name == "Class" &&
-                directive.Namespace == XamlNamespaces.Xaml2006 &&
-                directive.Values[0] is XamlAstTextNode text)
+            if (child is XamlAstXmlDirective { Name: "Class", Namespace: XamlNamespaces.Xaml2006 } directive
+                && directive.Values[0] is XamlAstTextNode text)
             {
-                if (_checkTypeValidity)
-                {
-                    var existingType = _typeSystem.FindType(text.Text);
-                    if (existingType == null)
-                    {
-                        _onTypeInvalid?.Invoke(text.Text);
-                        return node;
-                    }
-                }
-
                 var split = text.Text.Split('.');
                 var nameSpace = string.Join(".", split.Take(split.Length - 1));
                 var className = split.Last();
 
-                _resolvedClass = new ResolvedView(className, clrType, nameSpace, _xaml!);
+                _resolvedClass = new ResolvedViewDocument(className, nameSpace, _xaml!);
                 return node;
             }
         }

+ 61 - 3
src/tools/Avalonia.Generators/Compiler/MiniCompiler.cs

@@ -1,6 +1,9 @@
 using System;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
+using System.Threading;
+using Avalonia.Generators.Common.Domain;
+using XamlX.Ast;
 using XamlX.Compiler;
 using XamlX.Emit;
 using XamlX.Transform;
@@ -14,7 +17,22 @@ internal sealed class MiniCompiler : XamlCompiler<object, IXamlEmitResult>
     public const string AvaloniaXmlnsDefinitionAttribute = "Avalonia.Metadata.XmlnsDefinitionAttribute";
 
     [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = TrimmingMessages.Roslyn)]
-    public static MiniCompiler CreateDefault(RoslynTypeSystem typeSystem, params string[] additionalTypes)
+    public static MiniCompiler CreateNoop()
+    {
+        var typeSystem = new NoopTypeSystem();
+        var mappings = new XamlLanguageTypeMappings(typeSystem);
+        var diagnosticsHandler = new XamlDiagnosticsHandler();
+
+        var configuration = new TransformerConfiguration(
+            typeSystem,
+            typeSystem.Assemblies.First(),
+            mappings,
+            diagnosticsHandler: diagnosticsHandler);
+        return new MiniCompiler(configuration);
+    }
+
+    [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = TrimmingMessages.Roslyn)]
+    public static MiniCompiler CreateRoslyn(RoslynTypeSystem typeSystem, params string[] additionalTypes)
     {
         var mappings = new XamlLanguageTypeMappings(typeSystem);
         foreach (var additionalType in additionalTypes)
@@ -29,7 +47,7 @@ internal sealed class MiniCompiler : XamlCompiler<object, IXamlEmitResult>
             diagnosticsHandler: diagnosticsHandler);
         return new MiniCompiler(configuration);
     }
-        
+
     private MiniCompiler(TransformerConfiguration configuration)
         : base(configuration, new XamlLanguageEmitMappings<object, IXamlEmitResult>(), false)
     {
@@ -38,9 +56,42 @@ internal sealed class MiniCompiler : XamlCompiler<object, IXamlEmitResult>
         Transformers.Add(new KnownDirectivesTransformer());
         Transformers.Add(new XamlIntrinsicsTransformer());
         Transformers.Add(new XArgumentsTransformer());
-        Transformers.Add(new TypeReferenceResolver());
     }
 
+    public IXamlTypeSystem TypeSystem => _configuration.TypeSystem;
+
+    public void TransformWithCancellation(XamlDocument doc, CancellationToken cancellationToken)
+    {
+        var ctx = CreateTransformationContext(doc);
+
+        var root = doc.Root;
+        ctx.RootObject = new XamlRootObjectNode((XamlAstObjectNode)root);
+        foreach (var transformer in Transformers)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+            ctx.VisitChildren(ctx.RootObject, transformer);
+            root = ctx.Visit(root, transformer);
+        }
+
+        foreach (var simplifier in SimplificationTransformers)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+            root = ctx.Visit(root, simplifier);
+        }
+
+        doc.Root = root;
+    }
+
+    public IXamlType ResolveXamlType(XamlXmlType type)
+    {
+        var clrTypeRef = TypeReferenceResolver.ResolveType(
+            new AstTransformationContext(_configuration, null), ToTypeRef(type));
+        return clrTypeRef.Type;
+
+        static XamlAstXmlTypeReference ToTypeRef(XamlXmlType type) => new(EmptyLineInfo.Instance,
+            type.XmlNamespace, type.Name, type.GenericArguments.Select(ToTypeRef));
+    }
+    
     protected override XamlEmitContext<object, IXamlEmitResult> InitCodeGen(
         IFileSource file,
         IXamlTypeBuilder<object> declaringType,
@@ -48,4 +99,11 @@ internal sealed class MiniCompiler : XamlCompiler<object, IXamlEmitResult>
         XamlRuntimeContext<object, IXamlEmitResult> context,
         bool needContextLocal) =>
         throw new NotSupportedException();
+
+    private class EmptyLineInfo : IXamlLineInfo
+    {
+        public static IXamlLineInfo Instance { get; } = new EmptyLineInfo();
+        public int Line { get => 0; set { } }
+        public int Position { get => 0; set { } }
+    }
 }

+ 22 - 0
src/tools/Avalonia.Generators/Compiler/NoopTypeSystem.cs

@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using XamlX.TypeSystem;
+
+namespace Avalonia.Generators.Compiler;
+
+internal class NoopTypeSystem : IXamlTypeSystem
+{
+    public IEnumerable<IXamlAssembly> Assemblies => [NoopAssembly.Instance];
+    public IXamlAssembly? FindAssembly(string substring) => null;
+    public IXamlType? FindType(string name) => XamlPseudoType.Unresolved(name);
+    public IXamlType? FindType(string name, string assembly) => XamlPseudoType.Unresolved(name);
+
+    internal class NoopAssembly : IXamlAssembly
+    {
+        public static NoopAssembly Instance { get; } = new();
+        public bool Equals(IXamlAssembly other) => ReferenceEquals(this, other);
+        public string Name { get; } = "Noop";
+        public IReadOnlyList<IXamlCustomAttribute> CustomAttributes { get; } = [];
+        public IXamlType? FindType(string fullName) => XamlPseudoType.Unresolved(fullName);
+    }
+}
+

+ 5 - 3
src/tools/Avalonia.Generators/Compiler/RoslynTypeSystem.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Diagnostics.CodeAnalysis;
 using System.Linq;
@@ -11,8 +12,9 @@ namespace Avalonia.Generators.Compiler;
 internal class RoslynTypeSystem : IXamlTypeSystem
 {
     private readonly List<IXamlAssembly> _assemblies = new();
+    private readonly ConcurrentDictionary<string, IXamlType?> _typeCache = new();
 
-    public RoslynTypeSystem(CSharpCompilation compilation)
+    public RoslynTypeSystem(Compilation compilation)
     {
         _assemblies.Add(new RoslynAssembly(compilation.Assembly));
 
@@ -34,9 +36,9 @@ internal class RoslynTypeSystem : IXamlTypeSystem
 
     [UnconditionalSuppressMessage("Trimming", "IL2092", Justification = TrimmingMessages.Roslyn)]
     public IXamlType? FindType(string name) =>
-        _assemblies
+        _typeCache.GetOrAdd(name, _ => _assemblies
             .Select(assembly => assembly.FindType(name))
-            .FirstOrDefault(type => type != null);
+            .FirstOrDefault(type => type != null));
 
     [UnconditionalSuppressMessage("Trimming", "IL2092", Justification = TrimmingMessages.Roslyn)]
     public IXamlType? FindType(string name, string assembly) =>

+ 0 - 44
src/tools/Avalonia.Generators/GeneratorContextExtensions.cs

@@ -1,44 +0,0 @@
-using System;
-using Microsoft.CodeAnalysis;
-
-namespace Avalonia.Generators;
-
-internal static class GeneratorContextExtensions
-{
-    private const string UnhandledErrorDescriptorId = "AXN0002";
-    private const string InvalidTypeDescriptorId = "AXN0001";
-
-    public static string GetMsBuildProperty(
-        this GeneratorExecutionContext context,
-        string name,
-        string defaultValue = "")
-    {
-        context.AnalyzerConfigOptions.GlobalOptions.TryGetValue($"build_property.{name}", out var value);
-        return value ?? defaultValue;
-    }
-
-    public static void ReportNameGeneratorUnhandledError(this GeneratorExecutionContext context, Exception error) =>
-        context.Report(UnhandledErrorDescriptorId,
-            "Unhandled exception occurred while generating typed Name references. " +
-            "Please file an issue: https://github.com/avaloniaui/Avalonia",
-            error.Message,
-            error.ToString());
-
-    public static void ReportNameGeneratorInvalidType(this GeneratorExecutionContext context, string typeName) =>
-        context.Report(InvalidTypeDescriptorId,
-            $"Avalonia x:Name generator was unable to generate names for type '{typeName}'. " +
-            $"The type '{typeName}' does not exist in the assembly.");
-
-    private static void Report(this GeneratorExecutionContext context, string id, string title, string? message = null, string? description = null) =>
-        context.ReportDiagnostic(
-            Diagnostic.Create(
-                new DiagnosticDescriptor(
-                    id: id,
-                    title: title,
-                    messageFormat: message ?? title,
-                    category: "Usage",
-                    defaultSeverity: DiagnosticSeverity.Error,
-                    isEnabledByDefault: true,
-                    description),
-                Location.None));
-}

+ 43 - 0
src/tools/Avalonia.Generators/GeneratorExtensions.cs

@@ -0,0 +1,43 @@
+using System;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Avalonia.Generators;
+
+internal static class GeneratorExtensions
+{
+    private const string UnhandledErrorDescriptorId = "AXN0002";
+    private const string InvalidTypeDescriptorId = "AXN0001";
+
+    public static string GetMsBuildProperty(
+        this AnalyzerConfigOptions options,
+        string name,
+        string defaultValue = "")
+    {
+        options.TryGetValue($"build_property.{name}", out var value);
+        return value ?? defaultValue;
+    }
+
+    public static DiagnosticDescriptor NameGeneratorUnhandledError(Exception error) => new(
+        UnhandledErrorDescriptorId,
+        title: "Unhandled exception occurred while generating typed Name references. " +
+               "Please file an issue: https://github.com/avaloniaui/Avalonia",
+        messageFormat: error.Message,
+        description: error.ToString(),
+        category: "Usage",
+        defaultSeverity: DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static DiagnosticDescriptor NameGeneratorInvalidType(string typeName) => new(
+        InvalidTypeDescriptorId,
+        title: $"Avalonia x:Name generator was unable to generate names for type '{typeName}'. " +
+               $"The type '{typeName}' does not exist in the assembly.",
+        messageFormat: $"Avalonia x:Name generator was unable to generate names for type '{typeName}'. " +
+                       $"The type '{typeName}' does not exist in the assembly.",
+        category: "Usage",
+        defaultSeverity: DiagnosticSeverity.Error,
+        isEnabledByDefault: true);
+
+    public static void Report(this SourceProductionContext context, DiagnosticDescriptor diagnostics) =>
+        context.ReportDiagnostic(Diagnostic.Create(diagnostics, Location.None));
+}

+ 49 - 34
src/tools/Avalonia.Generators/GeneratorOptions.cs

@@ -1,7 +1,8 @@
 using System;
+using Avalonia.Generators.Common;
 using Avalonia.Generators.Common.Domain;
 using Avalonia.Generators.NameGenerator;
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
 
 namespace Avalonia.Generators;
 
@@ -18,58 +19,72 @@ internal enum BuildProperties
     // TODO add other generators properties here.
 }
 
-internal class GeneratorOptions
+internal record GeneratorOptions
 {
-    private readonly GeneratorExecutionContext _context;
-
-    public GeneratorOptions(GeneratorExecutionContext context) => _context = context;
+    public GeneratorOptions(AnalyzerConfigOptions options)
+    {
+        AvaloniaNameGeneratorIsEnabled = GetBoolProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorIsEnabled,
+            true);
+        AvaloniaNameGeneratorBehavior = GetEnumProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorBehavior,
+            Behavior.InitializeComponent);
+        AvaloniaNameGeneratorClassFieldModifier = GetEnumProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorDefaultFieldModifier,
+            NamedFieldModifier.Internal);
+        AvaloniaNameGeneratorViewFileNamingStrategy = GetEnumProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorViewFileNamingStrategy,
+            ViewFileNamingStrategy.NamespaceAndClassName);
+        AvaloniaNameGeneratorFilterByPath = new GlobPatternGroup(GetStringArrayProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorFilterByPath,
+            "*"));
+        AvaloniaNameGeneratorFilterByNamespace = new GlobPatternGroup(GetStringArrayProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorFilterByNamespace,
+            "*"));
+        AvaloniaNameGeneratorAttachDevTools = GetBoolProperty(
+            options,
+            BuildProperties.AvaloniaNameGeneratorAttachDevTools,
+            true);
+    }
 
-    public bool AvaloniaNameGeneratorIsEnabled => GetBoolProperty(
-        BuildProperties.AvaloniaNameGeneratorIsEnabled,
-        true);
+    public bool AvaloniaNameGeneratorIsEnabled { get; }
     
-    public Behavior AvaloniaNameGeneratorBehavior => GetEnumProperty(
-        BuildProperties.AvaloniaNameGeneratorBehavior,
-        Behavior.InitializeComponent);
+    public Behavior AvaloniaNameGeneratorBehavior { get; }
 
-    public NamedFieldModifier AvaloniaNameGeneratorClassFieldModifier => GetEnumProperty(
-        BuildProperties.AvaloniaNameGeneratorDefaultFieldModifier,
-        NamedFieldModifier.Internal);
+    public NamedFieldModifier AvaloniaNameGeneratorClassFieldModifier { get; }
 
-    public ViewFileNamingStrategy AvaloniaNameGeneratorViewFileNamingStrategy => GetEnumProperty(
-        BuildProperties.AvaloniaNameGeneratorViewFileNamingStrategy,
-        ViewFileNamingStrategy.NamespaceAndClassName);
+    public ViewFileNamingStrategy AvaloniaNameGeneratorViewFileNamingStrategy { get; }
 
-    public string[] AvaloniaNameGeneratorFilterByPath => GetStringArrayProperty(
-        BuildProperties.AvaloniaNameGeneratorFilterByPath,
-        "*");
+    public IGlobPattern AvaloniaNameGeneratorFilterByPath { get; }
 
-    public string[] AvaloniaNameGeneratorFilterByNamespace => GetStringArrayProperty(
-        BuildProperties.AvaloniaNameGeneratorFilterByNamespace,
-        "*");
+    public IGlobPattern AvaloniaNameGeneratorFilterByNamespace { get; }
 
-    public bool AvaloniaNameGeneratorAttachDevTools => GetBoolProperty(
-        BuildProperties.AvaloniaNameGeneratorAttachDevTools,
-        true);
+    public bool AvaloniaNameGeneratorAttachDevTools { get; }
 
-    private string[] GetStringArrayProperty(BuildProperties name, string defaultValue)
+    private static string[] GetStringArrayProperty(AnalyzerConfigOptions options, BuildProperties name, string defaultValue)
     {
         var key = name.ToString();
-        var value = _context.GetMsBuildProperty(key, defaultValue);
-        return value.Contains(";") ? value.Split(';') : new[] {value};
+        var value = options.GetMsBuildProperty(key, defaultValue);
+        return value.Contains(";") ? value.Split(';') : [value];
     }
 
-    private TEnum GetEnumProperty<TEnum>(BuildProperties name, TEnum defaultValue) where TEnum : struct
+    private static TEnum GetEnumProperty<TEnum>(AnalyzerConfigOptions options, BuildProperties name, TEnum defaultValue) where TEnum : struct
     {
         var key = name.ToString();
-        var value = _context.GetMsBuildProperty(key, defaultValue.ToString());
+        var value = options.GetMsBuildProperty(key, defaultValue.ToString());
         return Enum.TryParse(value, true, out TEnum behavior) ? behavior : defaultValue;
     }
-    
-    private bool GetBoolProperty(BuildProperties name, bool defaultValue)
+
+    private static bool GetBoolProperty(AnalyzerConfigOptions options, BuildProperties name, bool defaultValue)
     {
         var key = name.ToString();
-        var value = _context.GetMsBuildProperty(key, defaultValue.ToString());
+        var value = options.GetMsBuildProperty(key, defaultValue.ToString());
         return bool.TryParse(value, out var result) ? result : defaultValue;
     }
 }

+ 0 - 66
src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameGenerator.cs

@@ -1,66 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using Avalonia.Generators.Common.Domain;
-using Microsoft.CodeAnalysis;
-
-namespace Avalonia.Generators.NameGenerator;
-
-internal class AvaloniaNameGenerator : INameGenerator
-{
-    private readonly ViewFileNamingStrategy _naming;
-    private readonly IGlobPattern _pathPattern;
-    private readonly IGlobPattern _namespacePattern;
-    private readonly IViewResolver _classes;
-    private readonly INameResolver _names;
-    private readonly ICodeGenerator _code;
-
-    public AvaloniaNameGenerator(
-        ViewFileNamingStrategy naming,
-        IGlobPattern pathPattern,
-        IGlobPattern namespacePattern,
-        IViewResolver classes,
-        INameResolver names,
-        ICodeGenerator code)
-    {
-        _naming = naming;
-        _pathPattern = pathPattern;
-        _namespacePattern = namespacePattern;
-        _classes = classes;
-        _names = names;
-        _code = code;
-    }
-
-    public IEnumerable<GeneratedPartialClass> GenerateNameReferences(IEnumerable<AdditionalText> additionalFiles, CancellationToken cancellationToken)
-    {
-        var resolveViews =
-            from file in additionalFiles
-            let filePath = file.Path
-            where (filePath.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase) ||
-                   filePath.EndsWith(".paml", StringComparison.OrdinalIgnoreCase) ||
-                   filePath.EndsWith(".axaml", StringComparison.OrdinalIgnoreCase)) &&
-                  _pathPattern.Matches(filePath)
-            let xaml = file.GetText(cancellationToken)?.ToString()
-            where xaml != null
-            let view = _classes.ResolveView(xaml)
-            where view != null && _namespacePattern.Matches(view.Namespace)
-            select view;
-
-        var query =
-            from view in resolveViews
-            let names = _names.ResolveNames(view.Xaml)
-            let code = _code.GenerateCode(view.ClassName, view.Namespace, view.XamlType, names)
-            let fileName = ResolveViewFileName(view, _naming)
-            select new GeneratedPartialClass(fileName, code);
-
-        return query;
-    }
-
-    private static string ResolveViewFileName(ResolvedView view, ViewFileNamingStrategy strategy) => strategy switch
-    {
-        ViewFileNamingStrategy.ClassName => $"{view.ClassName}.g.cs",
-        ViewFileNamingStrategy.NamespaceAndClassName => $"{view.Namespace}.{view.ClassName}.g.cs",
-        _ => throw new ArgumentOutOfRangeException(nameof(strategy), strategy, "Unknown naming strategy!")
-    };
-}

+ 211 - 0
src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameIncrementalGenerator.cs

@@ -0,0 +1,211 @@
+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 XamlX.Transform;
+
+namespace Avalonia.Generators.NameGenerator;
+
+[Generator(LanguageNames.CSharp)]
+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)
+    {
+        // Map MSBuild properties onto readonly GeneratorOptions.
+        var options = context.AnalyzerConfigOptionsProvider
+            .Select(static (options, _) => new GeneratorOptions(options.GlobalOptions))
+            .WithTrackingName(TrackingNames.XamlGeneratorOptionsProvider);
+
+        // Filter additional texts, we only need Avalonia XAML files.
+        var xamlFiles = context.AdditionalTextsProvider
+            .Combine(options.Combine(context.AnalyzerConfigOptionsProvider))
+            .Where(static pair =>
+            {
+                var text = pair.Left;
+                var (options, optionsProvider) = pair.Right;
+                var filePath = text.Path;
+
+                if (!(filePath.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase) ||
+                      filePath.EndsWith(".paml", StringComparison.OrdinalIgnoreCase) ||
+                      filePath.EndsWith(".axaml", StringComparison.OrdinalIgnoreCase)))
+                {
+                    return false;
+                }
+
+                if (!options.AvaloniaNameGeneratorFilterByPath.Matches(filePath))
+                {
+                    return false;
+                }
+
+                if (!optionsProvider.GetOptions(pair.Left).TryGetValue(SourceItemGroupMetadata, out var itemGroup)
+                    || itemGroup != "AvaloniaXaml")
+                {
+                    return false;
+                }
+
+                return true;
+            })
+            .Select(static (pair, _) => pair.Left)
+            .WithTrackingName(TrackingNames.InputXamlFilesProvider);
+
+        // 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) =>
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+                var text = file.GetText(cancellationToken);
+                var diagnostics = new List<DiagnosticDescriptor>();
+                if (text is not null)
+                {
+                    try
+                    {
+                        var xaml = text.ToString();
+                        var viewResolver = new XamlXViewResolver(s_noopCompiler);
+                        var view = viewResolver.ResolveView(xaml, cancellationToken);
+                        if (view is null)
+                        {
+                            return null;
+                        }
+
+                        var nameResolver = new XamlXNameResolver();
+                        var xmlNames = nameResolver.ResolveXmlNames(view.Xaml, cancellationToken);
+
+                        return new XmlClassInfo(
+                            new ResolvedXmlView(view, xmlNames),
+                            new EquatableList<DiagnosticDescriptor>(diagnostics));
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        throw;
+                    }
+                    catch (Exception ex)
+                    {
+                        diagnostics.Add(GeneratorExtensions.NameGeneratorUnhandledError(ex));
+                        return new XmlClassInfo(null, new EquatableList<DiagnosticDescriptor>(diagnostics));
+                    }
+                }
+
+                return null;
+            })
+            .Where(request => request is not null)
+            .WithTrackingName(TrackingNames.ParsedXamlClasses);
+
+        // IMPORTANT: we shouldn't cache CompilationProvider as a whole,
+        // But we also should keep in mind that CompilationProvider can frequently re-trigger generator.
+        var compiler = context.CompilationProvider
+            .Select(static (compilation, _) =>
+            {
+                var roslynTypeSystem = new RoslynTypeSystem(compilation);
+                return MiniCompiler.CreateRoslyn(roslynTypeSystem, MiniCompiler.AvaloniaXmlnsDefinitionAttribute);
+            })
+            .WithTrackingName(TrackingNames.XamlTypeSystem);
+
+        // 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
+            .Combine(compiler)
+            .Select(static (pair, ct) =>
+            {
+                var (classInfo, compiler) = pair;
+                var hasDevToolsReference = compiler.TypeSystem.FindAssembly("Avalonia.Diagnostics") is not null;
+                var nameResolver = new XamlXNameResolver();
+
+                var diagnostics =  new List<DiagnosticDescriptor>(classInfo!.Diagnostics);
+                ResolvedView? view = null;
+                if (classInfo.XmlView is { } xmlView)
+                {
+                    var type = compiler.TypeSystem.FindType(xmlView.FullName);
+
+                    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)
+                        {
+                            ct.ThrowIfCancellationRequested();
+
+                            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(), new EquatableList<ResolvedName>(resolvedNames));
+                    }
+                }
+
+                return new ResolvedClassInfo(view, hasDevToolsReference, new EquatableList<DiagnosticDescriptor>(diagnostics));
+            })
+            .WithTrackingName(TrackingNames.ResolvedNamesProvider);
+
+        context.RegisterSourceOutput(resolvedNames.Combine(options), static (context, pair) =>
+        {
+            var (info, options) = pair;
+
+            foreach (var diagnostic in info!.Diagnostics)
+            {
+                context.Report(diagnostic);
+            }
+
+            if (info.View is { } view && options.AvaloniaNameGeneratorFilterByNamespace.Matches(view.Namespace))
+            {
+                ICodeGenerator codeGenerator = options.AvaloniaNameGeneratorBehavior switch
+                {
+                    Behavior.OnlyProperties => new OnlyPropertiesCodeGenerator(
+                        options.AvaloniaNameGeneratorClassFieldModifier),
+                    Behavior.InitializeComponent => new InitializeComponentCodeGenerator(
+                        options.AvaloniaNameGeneratorAttachDevTools && info.CanAttachDevTools && view.IsWindow,
+                        options.AvaloniaNameGeneratorClassFieldModifier),
+                    _ => throw new ArgumentOutOfRangeException()
+                };
+                var fileName = options.AvaloniaNameGeneratorViewFileNamingStrategy switch
+                {
+                    ViewFileNamingStrategy.ClassName => $"{view.ClassName}.g.cs",
+                    ViewFileNamingStrategy.NamespaceAndClassName => $"{view.Namespace}.{view.ClassName}.g.cs",
+                    _ => throw new ArgumentOutOfRangeException(
+                        nameof(ViewFileNamingStrategy), options.AvaloniaNameGeneratorViewFileNamingStrategy,
+                        "Unknown naming strategy!")
+                };
+
+                var generatedPartialClass = codeGenerator.GenerateCode(
+                    info.View.ClassName,
+                    info.View.Namespace,
+                    info.View.Names);
+
+                context.AddSource(fileName, generatedPartialClass);
+            }
+        });
+    }
+
+    internal record XmlClassInfo(
+        ResolvedXmlView? XmlView,
+        EquatableList<DiagnosticDescriptor> Diagnostics);
+
+    internal record ResolvedClassInfo(
+        ResolvedView? View,
+        bool CanAttachDevTools,
+        EquatableList<DiagnosticDescriptor> Diagnostics);
+}

+ 0 - 86
src/tools/Avalonia.Generators/NameGenerator/AvaloniaNameSourceGenerator.cs

@@ -1,86 +0,0 @@
-using System;
-using System.Collections.Generic;
-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;
-
-[Generator]
-public class AvaloniaNameSourceGenerator : ISourceGenerator
-{
-    private const string SourceItemGroupMetadata = "build_metadata.AdditionalFiles.SourceItemGroup";
-
-    public void Initialize(GeneratorInitializationContext context) { }
-
-    public void Execute(GeneratorExecutionContext context)
-    {
-        try
-        {
-            var generator = CreateNameGenerator(context);
-            if (generator is null)
-            {
-                return;
-            }
-
-            var partials = generator.GenerateNameReferences(ResolveAdditionalFiles(context), context.CancellationToken);
-            foreach (var (fileName, content) in partials)
-            {
-                if(context.CancellationToken.IsCancellationRequested)
-                {
-                    break;
-                }
-
-                context.AddSource(fileName, content);
-            }
-        }
-        catch (OperationCanceledException)
-        {
-        }
-        catch (Exception exception)
-        {
-            context.ReportNameGeneratorUnhandledError(exception);
-        }
-    }
-
-    private static IEnumerable<AdditionalText> ResolveAdditionalFiles(GeneratorExecutionContext context)
-    {
-        return context
-            .AdditionalFiles
-            .Where(f => context.AnalyzerConfigOptions
-                .GetOptions(f)
-                .TryGetValue(SourceItemGroupMetadata, out var sourceItemGroup)
-                && sourceItemGroup == "AvaloniaXaml");
-    }
-
-    private static INameGenerator? CreateNameGenerator(GeneratorExecutionContext context)
-    {
-        var options = new GeneratorOptions(context);
-        if (!options.AvaloniaNameGeneratorIsEnabled)
-        {
-            return null;
-        }
-
-        var types = new RoslynTypeSystem((CSharpCompilation)context.Compilation);
-        ICodeGenerator generator = options.AvaloniaNameGeneratorBehavior switch {
-            Behavior.OnlyProperties => new OnlyPropertiesCodeGenerator(),
-            Behavior.InitializeComponent => new InitializeComponentCodeGenerator(types, options.AvaloniaNameGeneratorAttachDevTools),
-            _ => throw new ArgumentOutOfRangeException()
-        };
-
-        var compiler = MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute);
-        return new AvaloniaNameGenerator(
-            options.AvaloniaNameGeneratorViewFileNamingStrategy,
-            new GlobPatternGroup(options.AvaloniaNameGeneratorFilterByPath),
-            new GlobPatternGroup(options.AvaloniaNameGeneratorFilterByNamespace),
-            new XamlXViewResolver(types, compiler, true,
-                type => context.ReportNameGeneratorInvalidType(type),
-                error => context.ReportNameGeneratorUnhandledError(error)),
-            new XamlXNameResolver(options.AvaloniaNameGeneratorClassFieldModifier),
-            generator);
-    }
-}

+ 3 - 9
src/tools/Avalonia.Generators/NameGenerator/INameGenerator.cs

@@ -1,12 +1,6 @@
-using System.Collections.Generic;
-using System.Threading;
-using Microsoft.CodeAnalysis;
+using Avalonia.Generators.Common;
+using Avalonia.Generators.Common.Domain;
+using Microsoft.CodeAnalysis.Text;
 
 namespace Avalonia.Generators.NameGenerator;
 
-internal interface INameGenerator
-{
-    IEnumerable<GeneratedPartialClass> GenerateNameReferences(IEnumerable<AdditionalText> additionalFiles, CancellationToken cancellationToken);
-}
-
-internal record GeneratedPartialClass(string FileName, string Content);

+ 5 - 24
src/tools/Avalonia.Generators/NameGenerator/InitializeComponentCodeGenerator.cs

@@ -1,14 +1,13 @@
 using System.Collections.Generic;
 using Avalonia.Generators.Common.Domain;
-using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.NameGenerator;
 
-internal class InitializeComponentCodeGenerator : ICodeGenerator
+internal class InitializeComponentCodeGenerator(bool avaloniaNameGeneratorAttachDevTools, NamedFieldModifier defaultNamedFieldModifier = NamedFieldModifier.Internal) : ICodeGenerator
 {
     private string _generatorName = typeof(InitializeComponentCodeGenerator).FullName;
     private string _generatorVersion = typeof(InitializeComponentCodeGenerator).Assembly.GetName().Version.ToString();
-    private readonly bool _diagnosticsAreConnected;
+
     private const string AttachDevToolsCodeBlock = @"
 #if DEBUG
             if (attachDevTools)
@@ -22,12 +21,7 @@ internal class InitializeComponentCodeGenerator : ICodeGenerator
 ";
 
 
-    public InitializeComponentCodeGenerator(IXamlTypeSystem types, bool avaloniaNameGeneratorAttachDevTools)
-    {
-        _diagnosticsAreConnected = avaloniaNameGeneratorAttachDevTools && types.FindAssembly("Avalonia.Diagnostics") != null;
-    }
-
-    public string GenerateCode(string className, string nameSpace, IXamlType xamlType, IEnumerable<ResolvedName> names)
+    public string GenerateCode(string className, string nameSpace, IEnumerable<ResolvedName> names)
     {
         var properties = new List<string>();
         var initializations = new List<string>();
@@ -45,7 +39,7 @@ internal class InitializeComponentCodeGenerator : ICodeGenerator
             var propertySource =
             $"""
                     [global::System.CodeDom.Compiler.GeneratedCode("{_generatorName}", "{_generatorVersion}")]
-                    {fieldModifier} {typeName} {name};
+                    {fieldModifier ?? defaultNamedFieldModifier.ToString().ToLowerInvariant()} {typeName} {name};
             """;
             properties.Add(propertySource);
             initializations.Add($"            {name} = __thisNameScope__?.Find<{typeName}>(\"{name}\");");
@@ -53,7 +47,7 @@ internal class InitializeComponentCodeGenerator : ICodeGenerator
             hasNames = true;
         }
 
-        var attachDevTools = _diagnosticsAreConnected && IsWindow(xamlType);
+        var attachDevTools = avaloniaNameGeneratorAttachDevTools;
 
         return $@"// <auto-generated />
 
@@ -87,17 +81,4 @@ namespace {nameSpace}
 }}
 ";
     }
-
-    private static bool IsWindow(IXamlType xamlType)
-    {
-        var type = xamlType;
-        bool isWindow;
-        do
-        {
-            isWindow = type.FullName == "Avalonia.Controls.Window";
-            type = type.BaseType;
-        } while (!isWindow && type != null);
-
-        return isWindow;
-    }
 }

+ 3 - 3
src/tools/Avalonia.Generators/NameGenerator/OnlyPropertiesCodeGenerator.cs

@@ -5,18 +5,18 @@ using XamlX.TypeSystem;
 
 namespace Avalonia.Generators.NameGenerator;
 
-internal class OnlyPropertiesCodeGenerator : ICodeGenerator
+internal class OnlyPropertiesCodeGenerator(NamedFieldModifier defaultNamedFieldModifier = NamedFieldModifier.Internal) : ICodeGenerator
 {
     private string _generatorName = typeof(OnlyPropertiesCodeGenerator).FullName;
     private string _generatorVersion = typeof(OnlyPropertiesCodeGenerator).Assembly.GetName().Version.ToString();
 
-    public string GenerateCode(string className, string nameSpace, IXamlType xamlType, IEnumerable<ResolvedName> names)
+    public string GenerateCode(string className, string nameSpace, IEnumerable<ResolvedName> names)
     {
         var namedControls = names
             .Select(info => "        " +
                             $"[global::System.CodeDom.Compiler.GeneratedCode(\"{_generatorName}\", \"{_generatorVersion}\")]\n" +
                             "        " +
-                            $"{info.FieldModifier} {info.TypeName} {info.Name} => " +
+                            $"{info.FieldModifier ?? defaultNamedFieldModifier.ToString().ToLowerInvariant()} {info.TypeName} {info.Name} => " +
                             $"this.FindNameScope()?.Find<{info.TypeName}>(\"{info.Name}\");")
             .ToList();
         var lines = string.Join("\n", namedControls);

+ 10 - 0
src/tools/Avalonia.Generators/NameGenerator/TrackingNames.cs

@@ -0,0 +1,10 @@
+namespace Avalonia.Generators.NameGenerator;
+
+internal static class TrackingNames
+{
+    public const string ResolvedNamesProvider = nameof(ResolvedNamesProvider);
+    public const string XamlGeneratorOptionsProvider = nameof(XamlGeneratorOptionsProvider);
+    public const string InputXamlFilesProvider = nameof(InputXamlFilesProvider);
+    public const string ParsedXamlClasses = nameof(ParsedXamlClasses);
+    public const string XamlTypeSystem = nameof(XamlTypeSystem);
+}

+ 1 - 20
src/tools/Avalonia.Generators/README.md

@@ -1,29 +1,10 @@
-[![NuGet Stats](https://img.shields.io/nuget/v/XamlNameReferenceGenerator.svg)](https://www.nuget.org/packages/XamlNameReferenceGenerator) [![downloads](https://img.shields.io/nuget/dt/XamlNameReferenceGenerator)](https://www.nuget.org/packages/XamlNameReferenceGenerator) ![Build](https://github.com/avaloniaui/Avalonia.NameGenerator/workflows/Build/badge.svg) ![License](https://img.shields.io/github/license/avaloniaui/Avalonia.NameGenerator.svg) ![Size](https://img.shields.io/github/repo-size/avaloniaui/Avalonia.NameGenerator.svg)
-
 ### C# `SourceGenerator` for Typed Avalonia `x:Name` References 
 
 This is a [C# `SourceGenerator`](https://devblogs.microsoft.com/dotnet/introducing-c-source-generators/) built for generating strongly-typed references to controls with `x:Name` (or just `Name`) attributes declared in XAML (or, in `.axaml`). The source generator will look for the `xaml` (or `axaml`) file with the same name as your partial C# class that is a subclass of `Avalonia.INamed` and parses the XAML markup, finds all XAML tags with `x:Name` attributes and generates the C# code.
 
 ### Getting Started
 
-In order to get started, just install the NuGet package:
-
-```
-dotnet add package XamlNameReferenceGenerator
-```
-
-Or, if you are using [submodules](https://git-scm.com/docs/git-submodule), you can reference the generator as such:
-
-```xml
-<ItemGroup>
-    <!-- Remember to ensure XAML files are included via <AdditionalFiles>,
-         otherwise C# source generator won't see XAML files. -->
-    <AdditionalFiles Include="**\*.xaml"/>
-    <ProjectReference Include="..\Avalonia.NameGenerator\Avalonia.NameGenerator.csproj"
-                      OutputItemType="Analyzer"
-                      ReferenceOutputAssembly="false" />
-</ItemGroup>
-```
+In order to get started, just create project with Avalonia NuGet package:
 
 ### Usage
 

+ 1 - 9
src/tools/DevAnalyzers/DevAnalyzers.csproj

@@ -3,15 +3,7 @@
   <PropertyGroup>
     <TargetFramework>netstandard2.0</TargetFramework>
     <Nullable>enable</Nullable>
-    <EnforceExtendedAnalyzerRules>True</EnforceExtendedAnalyzerRules>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
-      <PrivateAssets>all</PrivateAssets>
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-    </PackageReference>
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
-  </ItemGroup>
-
+  <Import Project="../../../build/AnalyzerProject.targets" />
 </Project>

+ 1 - 7
src/tools/DevGenerators/DevGenerators.csproj

@@ -4,18 +4,12 @@
     <TargetFramework>netstandard2.0</TargetFramework>
     <Nullable>enable</Nullable>
     <IsPackable>false</IsPackable>
-    <EnforceExtendedAnalyzerRules>True</EnforceExtendedAnalyzerRules>
-    <IsRoslynComponent>true</IsRoslynComponent>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-      <PrivateAssets>all</PrivateAssets>
-    </PackageReference>
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
     <Compile Include="..\..\Shared\SourceGeneratorAttributes.cs" />
     <Compile Include="..\..\Shared\IsExternalInit.cs" Link="IsExternalInit.cs" />
   </ItemGroup>
 
+  <Import Project="../../../build/AnalyzerProject.targets" />
 </Project>

+ 1 - 1
tests/Avalonia.Generators.Tests/Avalonia.Generators.Tests.csproj

@@ -10,8 +10,8 @@
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.ReactiveUI\Avalonia.ReactiveUI.csproj" />
     <ProjectReference Include="..\..\src\tools\Avalonia.Generators\Avalonia.Generators.csproj" />
-    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
     <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
+    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Views\*.xml" />

+ 24 - 0
tests/Avalonia.Generators.Tests/CompilationUtils.cs

@@ -0,0 +1,24 @@
+using System.Collections.Generic;
+using System.Linq;
+using Avalonia.Generators.Common;
+using Avalonia.Generators.Common.Domain;
+using Avalonia.Generators.Compiler;
+using Microsoft.CodeAnalysis;
+
+namespace Avalonia.Generators.Tests;
+
+internal static class CompilationUtils
+{
+    internal static IEnumerable<ResolvedName> ResolveNames(this IEnumerable<ResolvedXmlName> names, Compilation compilation, XamlXNameResolver nameResolver)
+    {
+        var compiler = MiniCompiler.CreateRoslyn(new RoslynTypeSystem(compilation), MiniCompiler.AvaloniaXmlnsDefinitionAttribute);
+        return names
+            .Select(xmlName =>
+            {
+                var clrType = compiler.ResolveXamlType(xmlName.XmlType);
+                return (clrType, nameResolver.ResolveName(clrType, xmlName.Name, xmlName.FieldModifier));
+            })
+            .Where(t => t.clrType.IsAvaloniaStyledElement())
+            .Select(t => t.Item2);
+    }
+}

+ 16 - 14
tests/Avalonia.Generators.Tests/InitializeComponent/InitializeComponentTests.cs

@@ -1,5 +1,8 @@
+using System.Linq;
+using System.Threading;
 using System.Threading.Tasks;
 using Avalonia.Generators.Common;
+using Avalonia.Generators.Common.Domain;
 using Avalonia.Generators.Compiler;
 using Avalonia.Generators.NameGenerator;
 using Avalonia.Generators.Tests.InitializeComponent.GeneratedInitializeComponent;
@@ -23,7 +26,6 @@ public class InitializeComponentTests
     [InlineData(InitializeComponentCode.FieldModifier, View.FieldModifier, false)]
     [InlineData(InitializeComponentCode.AttachedPropsWithDevTools, View.AttachedProps, true)]
     [InlineData(InitializeComponentCode.AttachedProps, View.AttachedProps, false)]
-    [InlineData(InitializeComponentCode.ControlWithoutWindow, View.ControlWithoutWindow, true)]
     [InlineData(InitializeComponentCode.ControlWithoutWindow, View.ControlWithoutWindow, false)]
     public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File(
         string expectation,
@@ -31,28 +33,28 @@ public class InitializeComponentTests
         bool devToolsMode)
     {
         var excluded = devToolsMode ? null : "Avalonia.Diagnostics";
-        var compilation =
-            View.CreateAvaloniaCompilation(excluded)
-                .WithCustomTextBox();
 
-        var types = new RoslynTypeSystem(compilation);
-        var classResolver = new XamlXViewResolver(
-            types,
-            MiniCompiler.CreateDefault(
-                new RoslynTypeSystem(compilation),
-                MiniCompiler.AvaloniaXmlnsDefinitionAttribute));
+        // Step 1: parse XAML as xml nodes, without any type information.
+        var classResolver = new XamlXViewResolver(MiniCompiler.CreateNoop());
 
         var xaml = await View.Load(markup);
-        var classInfo = classResolver.ResolveView(xaml);
+        var classInfo = classResolver.ResolveView(xaml, CancellationToken.None);
         Assert.NotNull(classInfo);
         var nameResolver = new XamlXNameResolver();
-        var names = nameResolver.ResolveNames(classInfo.Xaml);
+        var names = nameResolver.ResolveXmlNames(classInfo.Xaml, CancellationToken.None);
+
+        // Step 2: use compilation context to resolve types
+        var compilation =
+            View.CreateAvaloniaCompilation(excluded)
+                .WithCustomTextBox();
+        var resolvedNames = names.ResolveNames(compilation, nameResolver).ToArray();
 
-        var generator = new InitializeComponentCodeGenerator(types, devToolsMode);
+        // Step 3: run generator
+        var generator = new InitializeComponentCodeGenerator(devToolsMode);
         var generatorVersion = typeof(InitializeComponentCodeGenerator).Assembly.GetName().Version?.ToString();
 
         var code = generator
-            .GenerateCode("SampleView", "Sample.App",  classInfo.XamlType, names)
+            .GenerateCode("SampleView", "Sample.App",  resolvedNames)
             .Replace("\r", string.Empty);
 
         var expected = (await InitializeComponentCode.Load(expectation))

+ 2 - 2
tests/Avalonia.Generators.Tests/MiniCompilerTests.cs

@@ -21,7 +21,7 @@ public class MiniCompilerTests
     {
         var xaml = XDocumentXamlParser.Parse(MiniValidXaml);
         var compilation = CreateBasicCompilation(MiniClass);
-        MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml);
+        MiniCompiler.CreateRoslyn(new RoslynTypeSystem(compilation)).Transform(xaml);
 
         Assert.NotNull(xaml.Root);
     }
@@ -31,7 +31,7 @@ public class MiniCompilerTests
     {
         var xaml = XDocumentXamlParser.Parse(AvaloniaXaml);
         var compilation = View.CreateAvaloniaCompilation();
-        MiniCompiler.CreateDefault(new RoslynTypeSystem(compilation)).Transform(xaml);
+        MiniCompiler.CreateRoslyn(new RoslynTypeSystem(compilation)).Transform(xaml);
 
         Assert.NotNull(xaml.Root);
     }

+ 15 - 12
tests/Avalonia.Generators.Tests/OnlyProperties/OnlyPropertiesTests.cs

@@ -1,5 +1,8 @@
+using System.Linq;
+using System.Threading;
 using System.Threading.Tasks;
 using Avalonia.Generators.Common;
+using Avalonia.Generators.Common.Domain;
 using Avalonia.Generators.Compiler;
 using Avalonia.Generators.NameGenerator;
 using Avalonia.Generators.Tests.OnlyProperties.GeneratedCode;
@@ -25,27 +28,27 @@ public class OnlyPropertiesTests
     [InlineData(OnlyPropertiesCode.ControlWithoutWindow, View.ControlWithoutWindow)]
     public async Task Should_Generate_FindControl_Refs_From_Avalonia_Markup_File(string expectation, string markup)
     {
-        var compilation =
-            View.CreateAvaloniaCompilation()
-                .WithCustomTextBox();
-
-        var classResolver = new XamlXViewResolver(
-            new RoslynTypeSystem(compilation),
-            MiniCompiler.CreateDefault(
-                new RoslynTypeSystem(compilation),
-                MiniCompiler.AvaloniaXmlnsDefinitionAttribute));
+        // Step 1: parse XAML as xml nodes, without any type information.
+        var classResolver = new XamlXViewResolver(MiniCompiler.CreateNoop());
 
         var xaml = await View.Load(markup);
-        var classInfo = classResolver.ResolveView(xaml);
+        var classInfo = classResolver.ResolveView(xaml, CancellationToken.None);
         Assert.NotNull(classInfo);
         var nameResolver = new XamlXNameResolver();
-        var names = nameResolver.ResolveNames(classInfo.Xaml);
+        var names = nameResolver.ResolveXmlNames(classInfo.Xaml, CancellationToken.None);
+
+        // Step 2: use compilation context to resolve types
+        var compilation =
+            View.CreateAvaloniaCompilation()
+                .WithCustomTextBox();
+        var resolvedNames = names.ResolveNames(compilation, nameResolver).ToArray();
 
+        // Step 3: run generator
         var generator = new OnlyPropertiesCodeGenerator();
         var generatorVersion = typeof(OnlyPropertiesCodeGenerator).Assembly.GetName().Version?.ToString();
 
         var code = generator
-            .GenerateCode("SampleView", "Sample.App",  classInfo.XamlType, names)
+            .GenerateCode("SampleView", "Sample.App", resolvedNames)
             .Replace("\r", string.Empty);
 
         var expected = (await OnlyPropertiesCode.Load(expectation))

+ 3 - 10
tests/Avalonia.Generators.Tests/XamlXClassResolverTests.cs

@@ -1,3 +1,4 @@
+using System.Threading;
 using System.Threading.Tasks;
 using Avalonia.Generators.Common;
 using Avalonia.Generators.Compiler;
@@ -23,17 +24,9 @@ public class XamlXClassResolverTests
     public async Task Should_Resolve_Base_Class_From_Xaml_File(string nameSpace, string className, string markup)
     {
         var xaml = await View.Load(markup);
-        var compilation = View
-            .CreateAvaloniaCompilation()
-            .WithCustomTextBox()
-            .WithBaseView();
+        var resolver = new XamlXViewResolver(MiniCompiler.CreateNoop());
 
-        var types = new RoslynTypeSystem(compilation);
-        var resolver = new XamlXViewResolver(
-            types,
-            MiniCompiler.CreateDefault(types, MiniCompiler.AvaloniaXmlnsDefinitionAttribute));
-
-        var resolvedClass = resolver.ResolveView(xaml);
+        var resolvedClass = resolver.ResolveView(xaml, CancellationToken.None);
         Assert.NotNull(resolvedClass);
         Assert.Equal(className, resolvedClass.ClassName);
         Assert.Equal(nameSpace, resolvedClass.Namespace);

+ 13 - 11
tests/Avalonia.Generators.Tests/XamlXNameResolverTests.cs

@@ -1,4 +1,6 @@
 using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
 using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.Generators.Common;
@@ -6,6 +8,7 @@ using Avalonia.Generators.Common.Domain;
 using Avalonia.Generators.Compiler;
 using Avalonia.ReactiveUI;
 using Avalonia.Generators.Tests.Views;
+using Microsoft.CodeAnalysis;
 using Xunit;
 
 namespace Avalonia.Generators.Tests;
@@ -123,20 +126,19 @@ public class XamlXNameResolverTests
 
     private static IReadOnlyList<ResolvedName> ResolveNames(string xaml)
     {
+        var nameResolver = new XamlXNameResolver();
+
+        // Step 1: parse XAML as xml nodes, without any type information.
+        var classResolver = new XamlXViewResolver(MiniCompiler.CreateNoop());
+        var classInfo = classResolver.ResolveView(xaml, CancellationToken.None);
+        Assert.NotNull(classInfo);
+        var names = nameResolver.ResolveXmlNames(classInfo.Xaml, CancellationToken.None);
+
+        // Step 2: use compilation context to resolve types
         var compilation =
             View.CreateAvaloniaCompilation()
                 .WithCustomTextBox()
                 .WithBaseView();
-
-        var classResolver = new XamlXViewResolver(
-            new RoslynTypeSystem(compilation),
-            MiniCompiler.CreateDefault(
-                new RoslynTypeSystem(compilation),
-                MiniCompiler.AvaloniaXmlnsDefinitionAttribute));
-
-        var classInfo = classResolver.ResolveView(xaml);
-        Assert.NotNull(classInfo);
-        var nameResolver = new XamlXNameResolver();
-        return nameResolver.ResolveNames(classInfo.Xaml);
+        return names.ResolveNames(compilation, nameResolver).ToArray();
     }
 }