<commit_before>using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace WalletWasabi.Fluent.Generators
{
	[Generator]
	public class AutoNotifyGenerator : ISourceGenerator
	{
		private const string AttributeText = @"// <auto-generated />
using System;

namespace WalletWasabi.Fluent
{
    [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    sealed class AutoNotifyAttribute : Attribute
    {
        public AutoNotifyAttribute()
        {
        }
        public string PropertyName { get; set; }
    }
}";

		public void Initialize(GeneratorInitializationContext context)
		{
			// System.Diagnostics.Debugger.Launch();
			context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
		}

		public void Execute(GeneratorExecutionContext context)
		{
			context.AddSource("AutoNotifyAttribute", SourceText.From(AttributeText, Encoding.UTF8));

			if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
			{
				return;
			}

			CSharpParseOptions options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;
			Compilation compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeText, Encoding.UTF8), options));

			INamedTypeSymbol attributeSymbol = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.AutoNotifyAttribute");
			INamedTypeSymbol notifySymbol = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");

			List<IFieldSymbol> fieldSymbols = new List<IFieldSymbol>();
			foreach (FieldDeclarationSyntax field in receiver.CandidateFields)
			{
				SemanticModel model = compilation.GetSemanticModel(field.SyntaxTree);
				foreach (VariableDeclaratorSyntax variable in field.Declaration.Variables)
				{
					IFieldSymbol fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;
					if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)))
					{
						fieldSymbols.Add(fieldSymbol);
					}
				}
			}

			foreach (IGrouping<INamedTypeSymbol, IFieldSymbol> group in fieldSymbols.GroupBy(f => f.ContainingType))
			{
				string classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, notifySymbol, context);
				context.AddSource($"{group.Key.Name}_AutoNotify.cs", SourceText.From(classSource, Encoding.UTF8));
			}
		}

		private string ProcessClass(INamedTypeSymbol classSymbol, List<IFieldSymbol> fields, ISymbol attributeSymbol, ISymbol notifySymbol, GeneratorExecutionContext context)
		{
			if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
			{
				return null;
			}

			string namespaceName = classSymbol.ContainingNamespace.ToDisplayString();

			var source = new StringBuilder($@"// <auto-generated />
namespace {namespaceName}
{{
    public partial class {classSymbol.Name} : {notifySymbol.ToDisplayString()}
    {{
");

			if (!classSymbol.Interfaces.Contains(notifySymbol))
			{
				source.Append("        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
			}

			foreach (IFieldSymbol fieldSymbol in fields)
			{<commit_msg>Do not inherit from INPC if not needed<commit_after>using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;

namespace WalletWasabi.Fluent.Generators
{
	[Generator]
	public class AutoNotifyGenerator : ISourceGenerator
	{
		private const string AttributeText = @"// <auto-generated />
using System;

namespace WalletWasabi.Fluent
{
    [AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
    sealed class AutoNotifyAttribute : Attribute
    {
        public AutoNotifyAttribute()
        {
        }
        public string PropertyName { get; set; }
    }
}";

		public void Initialize(GeneratorInitializationContext context)
		{
			// System.Diagnostics.Debugger.Launch();
			context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
		}

		public void Execute(GeneratorExecutionContext context)
		{
			context.AddSource("AutoNotifyAttribute", SourceText.From(AttributeText, Encoding.UTF8));

			if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
			{
				return;
			}

			CSharpParseOptions options = (context.Compilation as CSharpCompilation).SyntaxTrees[0].Options as CSharpParseOptions;
			Compilation compilation = context.Compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(SourceText.From(AttributeText, Encoding.UTF8), options));

			INamedTypeSymbol attributeSymbol = compilation.GetTypeByMetadataName("WalletWasabi.Fluent.AutoNotifyAttribute");
			INamedTypeSymbol notifySymbol = compilation.GetTypeByMetadataName("System.ComponentModel.INotifyPropertyChanged");

			List<IFieldSymbol> fieldSymbols = new List<IFieldSymbol>();
			foreach (FieldDeclarationSyntax field in receiver.CandidateFields)
			{
				SemanticModel model = compilation.GetSemanticModel(field.SyntaxTree);
				foreach (VariableDeclaratorSyntax variable in field.Declaration.Variables)
				{
					IFieldSymbol fieldSymbol = model.GetDeclaredSymbol(variable) as IFieldSymbol;
					if (fieldSymbol.GetAttributes().Any(ad => ad.AttributeClass.Equals(attributeSymbol, SymbolEqualityComparer.Default)))
					{
						fieldSymbols.Add(fieldSymbol);
					}
				}
			}

			foreach (IGrouping<INamedTypeSymbol, IFieldSymbol> group in fieldSymbols.GroupBy(f => f.ContainingType))
			{
				string classSource = ProcessClass(group.Key, group.ToList(), attributeSymbol, notifySymbol, context);
				context.AddSource($"{group.Key.Name}_AutoNotify.cs", SourceText.From(classSource, Encoding.UTF8));
			}
		}

		private string ProcessClass(INamedTypeSymbol classSymbol, List<IFieldSymbol> fields, ISymbol attributeSymbol, ISymbol notifySymbol, GeneratorExecutionContext context)
		{
			if (!classSymbol.ContainingSymbol.Equals(classSymbol.ContainingNamespace, SymbolEqualityComparer.Default))
			{
				return null;
			}

			string namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
			bool addNotifyInterface = !classSymbol.Interfaces.Contains(notifySymbol);

			var source = new StringBuilder();

			if (addNotifyInterface)
			{
				source.Append($@"// <auto-generated />
namespace {namespaceName}
{{
    public partial class {classSymbol.Name} : {notifySymbol.ToDisplayString()}
    {{
");
				source.Append(
					"        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
			}
			else
			{
				source.Append($@"// <auto-generated />
namespace {namespaceName}
{{
    public partial class {classSymbol.Name}
    {{
");
			}

			foreach (IFieldSymbol fieldSymbol in fields)
			{
{'fraction_non_alphanumeric': 0.09821685360943341, 'fraction_numerical': 0.0011504170261719873, 'mean_word_length': 4.92924126172208, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2363861', 'n_tokens_mistral': 2129, 'n_tokens_neox': 1871, 'n_words': 366}