如何为字段的注释生成 Dart 代码?
Posted
技术标签:
【中文标题】如何为字段的注释生成 Dart 代码?【英文标题】:How to generate Dart code for annotations at fields? 【发布时间】:2020-02-29 04:36:42 【问题描述】:我正在使用 build_runner
为 Dart 编写代码生成器,但我的构建器不会被调用以用于字段的注释,尽管它确实适用于类的注释。
是否也可以在字段(或任何地方)调用注释生成器?
例如,为以下文件调用构建器:
import 'package:my_annotation/my_annotation.dart';
part 'example.g.dart';
@MyAnnotation()
class Fruit
int number;
但不是这个:
import 'package:my_annotation/my_annotation.dart';
part 'example.g.dart';
class Fruit
@MyAnnotation()
int number;
下面是注解的定义:
class MyAnnotation
const MyAnnotation();
这就是生成器的定义方式。目前,它只是在被调用时中止,从而导致打印错误消息。
library my_annotation_generator;
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:my_annotation/my_annotation.dart';
import 'package:source_gen/source_gen.dart';
Builder generateAnnotation(BuilderOptions options) =>
SharedPartBuilder([MyAnnotationGenerator()], 'my_annotation');
class MyAnnotationGenerator extends GeneratorForAnnotation<MyAnnotation>
@override
generateForAnnotatedElement(Element element, ConstantReader annotation, _)
throw CodeGenError('Generating code for annotation is not implemented yet.');
这是build.yaml
的配置:
targets:
$default:
builders:
my_annotation_generator|my_annotation:
enabled: true
builders:
my_annotation:
target: ":my_annotation_generator"
import: "package:my_annotation/my_annotation.dart"
builder_factories: ["generateAnnotation"]
build_extensions: ".dart": [".my_annotation.g.part"]
auto_apply: dependents
build_to: cache
applies_builders: ["source_gen|combining_builder"]
【问题讨论】:
【参考方案1】:至少根据我的经验,您的文件“example.dart”需要在类定义上方至少有一个注释才能由 GeneratorForAnnotation 解析。
example.dart:
import 'package:my_annotation/my_annotation.dart';
part 'example.g.dart';
@MyAnnotation()
class Fruit
@MyFieldAnnotation()
int number;
要访问类字段或类方法上方的注释,您可以使用访问者“访问”每个子元素并提取源代码信息。 例如,要获取有关类字段的信息,您可以覆盖方法 visitFieldElement,然后使用 getter:element.metadata 访问任何注释。
builder.dart:
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/visitor.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:build/src/builder/build_step.dart';
import 'package:source_gen/source_gen.dart';
import 'package:my_annotation/my_annotation.dart';
class MyAnnotationGenerator extends
GeneratorForAnnotation<MyAnnotation>
@override
FutureOr<String> generateForAnnotatedElement(
Element element,
ConstantReader annotation,
BuildStep buildStep,)
return _generateSource(element);
String _generateSource(Element element)
var visitor = ModelVisitor();
element.visitChildren(visitor);
return '''
// $visitor.className
// $visitor.fields
// $visitor.metaData
''';
class ModelVisitor extends SimpleElementVisitor
DartType className;
Map<String, DartType> fields = ;
Map<String, dynamic> metaData = ;
@override
visitConstructorElement(ConstructorElement element)
className = element.type.returnType;
@override
visitFieldElement(FieldElement element)
fields[element.name] = element.type;
metaData[element.name] = element.metadata;
注意:在本例中,_generateSource 返回一个注释语句。如果没有 cmets,您将需要返回格式良好的 dart 源代码,否则,构建器将因错误而终止。
有关详细信息,请参阅: 源代码生成和编写自己的包(无聊的 Flutter 开发秀,第 22 集)https://www.youtube.com/watch?v=mYDFOdl-aWM&t=459s
【讨论】:
太好了,谢谢!我最终没有使用只能检测***注释的GeneratorForAnnotation
,而是生成了一个自定义的Generator
,它解析了所有字段。你可以在这里看到它的实际效果:pub.dev/packages/has_is_getters
@Marcel,感谢您提供项目链接。请考虑在这里简要回答您自己的问题。虽然可以在 github.com 上找到代码生成示例,但仍然缺乏针对那些开始使用 Dart 源代码生成的文档。
元数据不包括字段注释,这是有意的,如果没有,我们如何访问字段注释?
当我尝试这个时,metadata
总是空的。后来我意识到我是在注释 getter 而不是字段。因此,如果您也遇到此问题,请检查您是否对字段进行了注释。如果您有 getter,则需要在访问者中实现 visitPropertyAccessorElement(PropertyAccessorElement element)
。【参考方案2】:
内置的GeneratorForAnnotation
使用LibraryElement
的annotatedWith(...)
方法,它只检查***注释。
要同时检测字段上的注释,您需要编写一些自定义内容。
这是我为我的项目写的Generator
:
abstract class GeneratorForAnnotatedField<AnnotationType> extends Generator
/// Returns the annotation of type [AnnotationType] of the given [element],
/// or [null] if it doesn't have any.
DartObject getAnnotation(Element element)
final annotations =
TypeChecker.fromRuntime(AnnotationType).annotationsOf(element);
if (annotations.isEmpty)
return null;
if (annotations.length > 1)
throw Exception(
"You tried to add multiple @$AnnotationType() annotations to the "
"same element ($element.name), but that's not possible.");
return annotations.single;
@override
String generate(LibraryReader library, BuildStep buildStep)
final values = <String>;
for (final element in library.allElements)
if (element is ClassElement && !element.isEnum)
for (final field in element.fields)
final annotation = getAnnotation(field);
if (annotation != null)
values.add(generateForAnnotatedField(
field,
ConstantReader(annotation),
));
return values.join('\n\n');
String generateForAnnotatedField(
FieldElement field, ConstantReader annotation);
【讨论】:
你是如何使用这个的?我们如何将这些结果嵌套在 GeneratorForAnnotation 的结果中?考虑一个带有注释的类,其中包含带有注释的字段【参考方案3】:我有一个非常相似的问题,试图在我的注释类中定位特定方法。受您的回答启发,我稍微修改了类注解model_visitor
,以便在选择元素之前检查方法注解。
class ClassAnnotationModelVisitor extends SimpleElementVisitor<dynamic>
String className;
Map<String, String> methods = <String, String>;
Map<String, String> parameters = <String, String>;
@override
dynamic visitConstructorElement(ConstructorElement element)
final elementReturnType = element.type.returnType.toString();
className = elementReturnType.replaceFirst('*', '');
@override
dynamic visitMethodElement(MethodElement element)
if (methodHasAnnotation(MethodAnnotation, element))
final functionReturnType = element.type.returnType.toString();
methods[element.name] = functionReturnType.replaceFirst('*', '');
parameters[element.name] = element.parameters.map((e) => e.name).join(' ,');
bool methodHasAnnotation(Type annotationType, MethodElement element)
final annotations = TypeChecker.fromRuntime(annotationType).annotationsOf(element);
return !annotations.isEmpty;
然后,我可以使用基本的GeneratorForAnnotation
类并为类和方法数组生成。
【讨论】:
以上是关于如何为字段的注释生成 Dart 代码?的主要内容,如果未能解决你的问题,请参考以下文章