Description
Let's imagine we have been provided with a GenerateFields
macro that generates the fields for constructor arguments to cut down on boilerplate. Developers can use it as follows:
/// Doc string about the MyObject.
///
/// It contains lots of facinating details.
/// Wow, this class is very well documented.
@GenerateFields()
class MyObject {
/// Doc string about the constructor.
MyObject({
/// Doc string about the integerValue.
///
/// Did you know that some integers have negative attitude?
int? integerValue,
/// Doc string about stringValue.
///
/// Strings are very interesting.
String? stringValue,
});
/// Doc string about this method.
///
/// It does lots of interesting things.
bool checkCondition() {
return integerValue == 42 && stringValue == 'forty-two';
}
// ... more methods here.
}
The code that the macro generates for use behind the scenes looks like this:
augment class MyObject {
final prefix0.int? integerValue;
final prefix0.String? stringValue;
augment MyObject({ prefix0.int? integerValue, prefix0.String? stringValue, })
: integerValue = integerValue,
stringValue = stringValue;
}
Developers now use instances of MyObject
in their code, example:
void main() {
final obj = MyObject(integerValue: 54);
if (obj.stringValue == 'Hello World') {
print(obj.checkCondition() ? 'Yes!' : 'No!');
}
}
As developers write the code snippet above they want to jump to the source of MyObject
, e.g. to read the docs or to figure out what other methods MyObject implements. So, they Ctrl+click on "MyObject" (or "stringValue" or "integerValue"). This takes them to the generated code, which is not very useful here and also kinda hard to read. It would have been much more useful to send the developer to the original MyObject
class that was authored by the developer itself. That's the code they are familiar with and it contains all the useful information. The generated code is more of an implementation detail, but reading it on a regular basis is not the primary use case for developers that simply use the macro. Which finally brings my to my request:
There should be a way (maybe an annotation) to configure the IDE click-through target for any entity in generated code. In our example, we'd want to annotate the constructor augmentation (augment MyObject(...)
) with something that sends click-throughs to the constructor in the original code. Similarly, the fields integerValue
and stringValue
in the generated code should be annotated with something that sends click-throughs to the corresponding constructor parameter in the original code. The target of the click-through should be configurable by the annotation and the annotations itself would be added by the macro.
Designing the mechanism for this in an open, configurable way independent of macros can also make them useful for other generated code use cases. An example for this are Protocol Buffers (protobufs). When you use protobufs in your dart code and you click through on the name of the protobuf or one of its fields you probably primarily want to go to the .proto file itself (it has all the useful documentation etc.). You probably do not want to go to the generated code that implements the marshaling and unmarshalling of the protobuf in Dart. That logic is sort of an implementation detail.
Last, but not least, an important caveat: While it can be useful to tuck the generated code out of sight and redirect click-throughs to a more appropriate location, the macro-generated code should never be completely hidden. The IDE should make it very clear (visually) that a class/method/thing is being augmented (e.g. with symbols in the margin) and provide an easy way to jump to the generated code. Similarly, stepping through with a debugger should not be affected in any way by these annotations. The proposal here only argues that in some instances the primary use case when clicking through in an IDE is not to go to the generated code, but to go to some other code location that has more context. It assumes that the author of the code generation mechanism (e.g. the macro) knows best what that location should be.