diff --git a/README.md b/README.md
index d401414ab4..469d51604e 100644
--- a/README.md
+++ b/README.md
@@ -6,20 +6,11 @@
-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 idea is that you include your Avalonia XAML files into your project via `` and then decorate your view class with `[GenerateTypedNameReferences]` and the source generator will look for the `xaml` (or `axaml`) file with the same name as your C# class. The source generator then parses the XML markup, finds all XML tags with `x:Name` attributes and generates the C# code.
+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 subclasse of `Avalonia.INambe` and parses the XML markup, finds all XML tags with `x:Name` attributes and generates the C# code.
### Getting Started
-So in your project file you paste the following code:
-
-```xml
-
-
-
-
-```
-
-And then you reference the source generator by installing a NuGet package:
+Add reference the source generator by installing a NuGet package:
```
dotnet add package XamlNameReferenceGenerator
@@ -33,14 +24,42 @@ Or, if you are using [submodules](https://git-scm.com/docs/git-submodule), you c
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
+
+
+
+```
+
+Finally, you declare your view class as `partial`
+
+```cs
+using Avalonia.Controls;
+
+public partial class SignUpView : Window
+{
+ public SignUpView()
+ {
+ AvaloniaXamlLoader.Load(this);
+ UserNameTextBox.Text = "Joseph"; // Coolstuff!
+ }
+}
+```
+
+If you just want specific classes:
+
+add at your csproj this lines:
+
+```xml
+
+ false
+
```
-Finally, you declare your view class as `partial` and decorate it with `[GenerateTypedNameReferences]`:
+Finally, decorate yours class with attribute `[GenerateTypedNameReferences]`:
```cs
using Avalonia.Controls;
-[GenerateTypedNameReferences] // Note the 'partial' keyword.
+[GenerateTypedNameReferences]
public partial class SignUpView : Window
{
public SignUpView()
diff --git a/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj b/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj
index 3e965b969b..4ec57fbe66 100644
--- a/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj
+++ b/src/Avalonia.NameGenerator/Avalonia.NameGenerator.csproj
@@ -5,6 +5,7 @@
true
false
XamlNameReferenceGenerator
+ true
@@ -14,6 +15,12 @@
+
+
+ true
+ buildTransitive\$(PackageId).props
+
+
diff --git a/src/Avalonia.NameGenerator/Generator.props b/src/Avalonia.NameGenerator/Generator.props
new file mode 100644
index 0000000000..f41d0800bd
--- /dev/null
+++ b/src/Avalonia.NameGenerator/Generator.props
@@ -0,0 +1,16 @@
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs b/src/Avalonia.NameGenerator/NameReferenceGenerator.cs
index f67e75f0f9..687608a738 100644
--- a/src/Avalonia.NameGenerator/NameReferenceGenerator.cs
+++ b/src/Avalonia.NameGenerator/NameReferenceGenerator.cs
@@ -15,6 +15,7 @@ namespace Avalonia.NameGenerator
[Generator]
public class NameReferenceGenerator : ISourceGenerator
{
+ private const string INamedType = "Avalonia.INamed";
private const string AttributeName = "GenerateTypedNameReferencesAttribute";
private const string AttributeFile = "GenerateTypedNameReferencesAttribute";
private const string AttributeCode = @"//
@@ -43,7 +44,7 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
return;
}
- var compilation = (CSharpCompilation) context.Compilation;
+ var compilation = (CSharpCompilation)context.Compilation;
var nameResolver = new XamlXNameResolver(compilation);
var nameGenerator = new FindControlNameGenerator();
var symbols = UnpackAnnotatedTypes(context, compilation, receiver);
@@ -108,6 +109,10 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
CSharpCompilation existingCompilation,
NameReferenceSyntaxReceiver nameReferenceSyntaxReceiver)
{
+ var allowedNameGenerator = context
+ .GetMSBuildProperty("AvaloniaNameGenerator", "false")
+ .Equals("true", StringComparison.OrdinalIgnoreCase);
+
var options = (CSharpParseOptions)existingCompilation.SyntaxTrees[0].Options;
var compilation = existingCompilation.AddSyntaxTrees(
CSharpSyntaxTree.ParseText(
@@ -119,15 +124,22 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
foreach (var candidateClass in nameReferenceSyntaxReceiver.CandidateClasses)
{
var model = compilation.GetSemanticModel(candidateClass.SyntaxTree);
- var typeSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(candidateClass);
- var relevantAttribute = typeSymbol!
- .GetAttributes()
- .FirstOrDefault(attr => attr.AttributeClass!.Equals(attributeSymbol, SymbolEqualityComparer.Default));
-
- if (relevantAttribute == null)
+ var typeSymbol = (INamedTypeSymbol)model.GetDeclaredSymbol(candidateClass);
+ if (InheritsFrom(typeSymbol, INamedType) == false)
{
continue;
}
+ if (allowedNameGenerator == false)
+ {
+ var relevantAttribute = typeSymbol!
+ .GetAttributes()
+ .FirstOrDefault(attr => attr.AttributeClass!.Equals(attributeSymbol, SymbolEqualityComparer.Default));
+
+ if (relevantAttribute == null)
+ {
+ continue;
+ }
+ }
var isPartial = candidateClass
.Modifiers
@@ -159,5 +171,31 @@ internal sealed class GenerateTypedNameReferencesAttribute : Attribute { }
return symbols;
}
+
+ static bool InheritsFrom(INamedTypeSymbol symbol, string typeName)
+ {
+ while (true)
+ {
+ if (symbol.ToString() == typeName)
+ {
+ return true;
+ }
+ if (symbol.BaseType != null)
+ {
+ var intefaces = symbol.AllInterfaces;
+ foreach (var @interface in intefaces)
+ {
+ if (@interface.ToString() == typeName)
+ {
+ return true;
+ }
+ }
+ symbol = symbol.BaseType;
+ continue;
+ }
+ break;
+ }
+ return false;
+ }
}
}
diff --git a/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs b/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs
index fec82c8e14..f74a54c467 100644
--- a/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs
+++ b/src/Avalonia.NameGenerator/NameReferenceSyntaxReceiver.cs
@@ -10,8 +10,7 @@ namespace Avalonia.NameGenerator
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
- if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax &&
- classDeclarationSyntax.AttributeLists.Count > 0)
+ if (syntaxNode is ClassDeclarationSyntax classDeclarationSyntax)
CandidateClasses.Add(classDeclarationSyntax);
}
}
diff --git a/src/Avalonia.NameGenerator/SourceGeneratorContextExtensions.cs b/src/Avalonia.NameGenerator/SourceGeneratorContextExtensions.cs
new file mode 100644
index 0000000000..6c30ac512f
--- /dev/null
+++ b/src/Avalonia.NameGenerator/SourceGeneratorContextExtensions.cs
@@ -0,0 +1,28 @@
+using Microsoft.CodeAnalysis;
+using System.Linq;
+
+namespace Avalonia.NameGenerator
+{
+ internal static class SourceGeneratorContextExtensions
+ {
+ private const string SourceItemGroupMetadata = "build_metadata.AdditionalFiles.SourceItemGroup";
+
+ 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 string[] GetMSBuildItems(this GeneratorExecutionContext context, string name)
+ => context
+ .AdditionalFiles
+ .Where(f => context.AnalyzerConfigOptions
+ .GetOptions(f).TryGetValue(SourceItemGroupMetadata, out var sourceItemGroup)
+ && sourceItemGroup == name)
+ .Select(f => f.Path)
+ .ToArray();
+ }
+}