66 public void Initialize(IncrementalGeneratorInitializationContext context)
68 IncrementalValueProvider<INamedTypeSymbol?> attributeSymbolProvider =
69 context.CompilationProvider.Select(
72 IncrementalValueProvider<ImmutableArray<CommandCandidateModel>> candidateProvider =
73 context.SyntaxProvider
74 .CreateSyntaxProvider(
75 predicate:
static (node, _) => node is MethodDeclarationSyntax { AttributeLists.Count: > 0 },
76 transform:
static (syntaxContext, _) => syntaxContext)
77 .Combine(attributeSymbolProvider)
79 .Where(
static candidate => candidate is not
null)
80 .Select(
static (candidate, _) => candidate!)
83 context.RegisterSourceOutput(candidateProvider,
static (sourceProductionContext, candidates) =>
85 Emit(sourceProductionContext, candidates);
96 GeneratorSyntaxContext context,
97 INamedTypeSymbol? attributeSymbol)
99 if (attributeSymbol is
null)
104 if (context.Node is not MethodDeclarationSyntax methodSyntax)
109 if (context.SemanticModel.GetDeclaredSymbol(methodSyntax) is not IMethodSymbol methodSymbol)
114 AttributeData? attributeData = methodSymbol
116 .FirstOrDefault(attribute => SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeSymbol));
118 if (attributeData is
null)
145 SourceProductionContext context,
146 ImmutableArray<CommandCandidateModel> candidates)
148 if (candidates.IsDefaultOrEmpty)
156 foreach (Diagnostic diagnostic
in diagnostics)
158 context.ReportDiagnostic(diagnostic);
161 if (diagnostics.Count > 0)
169 context.AddSource(fileName, SourceText.From(source, Encoding.UTF8));
180 List<Diagnostic> diagnostics =
new List<Diagnostic>();
182 bool hasTargetMethod = !
string.IsNullOrWhiteSpace(candidate.
TargetMethod);
186 diagnostics.Add(Diagnostic.Create(
194 diagnostics.Add(Diagnostic.Create(
197 candidate.
MethodSymbol.ContainingType.ToDisplayString()));
202 diagnostics.Add(Diagnostic.Create(
210 diagnostics.Add(Diagnostic.Create(
214 candidate.
MethodSymbol.ContainingType.ToDisplayString()));
220 diagnostics.Add(Diagnostic.Create(
224 candidate.
MethodSymbol.ContainingType.ToDisplayString()));
331 string namespaceName = candidate.MethodSymbol.ContainingNamespace.IsGlobalNamespace
333 : candidate.
MethodSymbol.ContainingNamespace.ToDisplayString();
337 string helperTypeName =
"__DreamineGeneratedCommand_" + candidate.
MethodSymbol.Name;
338 bool hasTargetMethod = !
string.IsNullOrWhiteSpace(candidate.
TargetMethod);
339 string? normalizedInvocation = hasTargetMethod
343 StringBuilder builder =
new StringBuilder();
344 builder.AppendLine(
"// <auto-generated />");
345 builder.AppendLine(
"#nullable enable");
346 builder.AppendLine(
"using System;");
347 builder.AppendLine(
"using System.Windows.Input;");
348 builder.AppendLine();
350 if (!
string.IsNullOrWhiteSpace(namespaceName))
352 builder.AppendLine(
"namespace " + namespaceName);
353 builder.AppendLine(
"{");
356 for (
int i = 0; i < typeChain.Count; i++)
358 string indent =
new string(
' ', 4 * (i + 1));
360 builder.AppendLine(indent +
"{");
363 string memberIndent =
new string(
' ', 4 * (typeChain.Count + 1));
365 builder.AppendLine(memberIndent +
"/// <summary>");
366 builder.AppendLine(memberIndent +
"/// 생성된 ICommand 프로퍼티입니다.");
367 builder.AppendLine(memberIndent +
"/// </summary>");
368 builder.AppendLine(memberIndent +
"private ICommand? " + commandFieldName +
";");
370 ? candidate.MethodSymbol.Name +
", " + candidate.CanExecuteMethod
372 builder.AppendLine(memberIndent +
"public ICommand " + candidate.
CommandPropertyName +
" => " + commandFieldName +
" ??= new " + helperTypeName +
"(" + ctorArgs +
");");
373 builder.AppendLine();
375 bool hasBody = candidate.MethodSyntax.Body is not
null || candidate.MethodSyntax.ExpressionBody is not
null;
376 if (hasTargetMethod && !hasBody)
379 builder.AppendLine(memberIndent +
"{");
381 if (!
string.IsNullOrWhiteSpace(candidate.
BindTo))
383 builder.AppendLine(memberIndent +
" var __result = " + normalizedInvocation +
";");
384 builder.AppendLine(memberIndent +
" " + candidate.
BindTo +
" = __result;");
388 builder.AppendLine(memberIndent +
" " + normalizedInvocation +
";");
391 builder.AppendLine(memberIndent +
"}");
392 builder.AppendLine();
395 bool hasCanExecute = !
string.IsNullOrWhiteSpace(candidate.
CanExecuteMethod);
397 builder.AppendLine(memberIndent +
"private sealed class " + helperTypeName +
" : ICommand");
398 builder.AppendLine(memberIndent +
"{");
399 builder.AppendLine(memberIndent +
" private readonly Action _execute;");
402 builder.AppendLine(memberIndent +
" private readonly Func<bool> _canExecute;");
403 builder.AppendLine(memberIndent +
" public " + helperTypeName +
"(Action execute, Func<bool> canExecute)");
404 builder.AppendLine(memberIndent +
" {");
405 builder.AppendLine(memberIndent +
" _execute = execute ?? throw new ArgumentNullException(nameof(execute));");
406 builder.AppendLine(memberIndent +
" _canExecute = canExecute ?? throw new ArgumentNullException(nameof(canExecute));");
407 builder.AppendLine(memberIndent +
" }");
411 builder.AppendLine(memberIndent +
" public " + helperTypeName +
"(Action execute)");
412 builder.AppendLine(memberIndent +
" {");
413 builder.AppendLine(memberIndent +
" _execute = execute ?? throw new ArgumentNullException(nameof(execute));");
414 builder.AppendLine(memberIndent +
" }");
417 builder.AppendLine(memberIndent +
" public event EventHandler? CanExecuteChanged;");
418 builder.AppendLine(memberIndent +
" public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);");
421 builder.AppendLine(memberIndent +
" public bool CanExecute(object? parameter) => _canExecute();");
425 builder.AppendLine(memberIndent +
" public bool CanExecute(object? parameter) => true;");
428 builder.AppendLine(memberIndent +
" public void Execute(object? parameter) => _execute();");
429 builder.AppendLine(memberIndent +
"}");
430 builder.AppendLine();
432 for (
int i = typeChain.Count - 1; i >= 0; i--)
434 string indent =
new string(
' ', 4 * (i + 1));
435 builder.AppendLine(indent +
"}");
438 if (!
string.IsNullOrWhiteSpace(namespaceName))
440 builder.AppendLine(
"}");
443 return builder.ToString();
535 string kind = typeSymbol.TypeKind
switch
537 TypeKind.Class =>
"partial class",
538 TypeKind.Struct =>
"partial struct",
542 string accessibility = typeSymbol.DeclaredAccessibility
switch
544 Accessibility.Public =>
"public",
545 Accessibility.Internal =>
"internal",
546 Accessibility.Private =>
"private",
547 Accessibility.Protected =>
"protected",
548 Accessibility.ProtectedAndInternal =>
"protected internal",
549 Accessibility.ProtectedOrInternal =>
"protected internal",
553 string staticKeyword = typeSymbol.IsStatic ?
" static" :
string.Empty;
555 string typeParameters = typeSymbol.TypeParameters.Length > 0
556 ?
"<" +
string.Join(
", ", typeSymbol.TypeParameters.Select(parameter => parameter.Name)) +
">"
559 return accessibility + staticKeyword +
" " + kind +
" " + typeSymbol.Name + typeParameters;