-
-
Notifications
You must be signed in to change notification settings - Fork 97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Annotations in GDScript #828
Comments
My 2cents on this.
Example C++:
Using GDScript autoconversion:
so what happened here?
This ensures we don't have to maintain a parallel system for exporting hints. Having special cases for exporting resource types or enums is fine, but as a base the autoconversion should just work. |
some other use cases I can think of ## to register class names
@class_name("MyClass")
## ignore warnings
@ignore_warning("unused_argument")
func _process(delta):
pass
## and for override functions
@override
func _to_string()->String():
return "str" |
I think this proposal looks great. I'd also like to emphasize that I think leaving room for a future proposal to enable user-defined exports is a good idea. It paves the way for people to design UX in their projects that is better optimized for use with their own custom tools. I prefer having to flip between code text and the editor GUI as little as possible. If I can write my own tools to have script code declaratively trigger behaviors in the editor based on what I tell my scripts to be or do, then that empowers me to code the way I want to, to build more powerful editor tools, etc. Of course, some practical use cases of user-defined signals should be outlined for it to actually be implemented. And again, that would likely be for a separate proposal after the release of 4.0. As for additional annotations to add to the list... # Class-level annotation.
# Adds group names to the Node upon instantiation.
# similar to `onready`, but tied directly to `add_to_group()`
@groups("enemy", "goomba")
# Likewise, a delayed registration for a signal connection.
# Must be applied to a variable with the onready annotation.
@onready
@connect("body_entered", self, "_on_body_entered", ["Hello"], CONNECT_ONESHOT)
var area := get_node("Area2D") as Area2D
# Method annotation.
# Allow users to easily export one-off buttons for the Inspector
# that execute a parameterless, void-returning function.
@export_button("Click Me!")
func _do_the_thing() -> void:
print("Hello World!") Exporting a method as a button is tracked in godotengine/godot#9380. |
I think being able to define and use custom annotations is the main attraction of them. I'd consider annotations just a way to assign inspectable metadata to properties, classes, etc. So what if I want to get the annotations assigned to a property? I'd guess I'll have to get the proper PropertyInfo and check if it has the annotation I'm looking for. |
I suspect that, rather than change how PropertyInfo objects are designed, annotations will instead be a language-specific feature. But in order for it to be usable by the editor at all, you still need some low-level Script interface method that exposes whatever metadata the annotations will define. This way, C#'s attributes and GDScript's annotations can both be capable of exposing the same information through metadata-specific callbacks. We would then modify the Editor to get property annotations by fetching the object's metadata rather than be getting the property list (or something of that nature). There should be no need to modify PropertyInfo to accommodate the changes while still enabling each language to have their own means of supplying metadata in a common interface. I'm still not in-the-know about the discussions on the topic though, so maybe a different methodology is planned? |
Class name won't be an annotation since it's part of the script itself. Override is something I thought in the past as well, potentially showing a warning if it's missing. |
Well, user-defined exports still needs to be handled by the editor in some fashion, so if/when this is done we can integrate in GDScript. Using annotations for export already make this potential addition easier IMO.
I'm okay with custom annotations, but there's no plan for that yet (that is I don't know how that would work). We'll need a second proposal with detailed use-cases so we can cover what's needed. Right now we could add a This will also be added for GDScript only, as each language has their own way of adding metadata, so it won't go PropertyInfo. You'll need a GDScript instance to be able to access this data, but that should be expected. |
I compiled all the export hints that makes sense to expose: @export # Basic: built-in types, resources, enums, typed arrays. Inferred from variable type.
@export_enum("values"...) # Custom ad-hoc enum, typed integer.
@export_file("filters"...) # Local file
@export_dir # Local dir.
@export_global_file("filters"...) # Global file
@export_global_dir # Global dir.
@export_multiline # Multiline string
@export_placeholder("placeholder") # Placeholder for text field
@export_range(min, max, step = 1, slider1 = "", slider2 = "") # Integer/float range. slider can be "or_greater", or "or_lesser"
@export_exp_range(min, max, step = 1, slider1 = "", slider2 = "") # Same as range, exponential slider
@export_exp_easing(hint1 = "", hint2 = "") # Exponential easing function (Math::ease), use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation", "inout")
@export_color_no_alpha # Color, but ignore alpha component in the editor
@export_node_path("type") # NodePath, but restrict to a certain Node type
@export_flags("values"...) # Bit flags (multiple values can be selected)
@export_flags_2d_render # 2D render layers selector (int)
@export_flags_2d_physics # 2D physics layers selector (int)
@export_flags_3d_render # 3D render layers selector (int)
@export_flags_3d_physics # 3D physics layers selector (int) The only one different from the PropertyHint enum is the Additionally, we can add special annotations for categories and groups: @export_category("category")
@export_group("group", "prefix") # All variables with name starting with "prefix" are added to the group. Those create internal properties with the correct |
@vnen I'm a bit concerned about annotations that both export the variable and also assign a special PropertyEditor in the inspector. I see how this could be resolved in two ways:
I think I prefer this second option, as it also keeps |
So, either a separate annotation or one that overrides the effect of another? I mean, all it really comes down to is, if you've got an annotation that is applied to a property, then the script logic implementing the annotation will need to have read/write access to the PropertyInfo generated for the property. At that point, you'd be able to implement either strategy, and it would be up to the user who is writing the custom annotation to determine whether they want it to be separate or an override. @vnen's work at the moment, to create the built-in annotations, will likely have little to no impact on the aspects you've distinguished above. |
That comes to how doable it is to expose such functionality to user defined annotations and how easily they can manipulate PropertyInfos. If complete freedom can be provided in a user-friendly way, then yes, the current built-in annotation naming is ok and the user could take either solution (even if built-in annotations would keep the @export_something naming scheme, so the same for custom ones should be recommended). Otherwise, a custom annotation couldn't be used like a built-in @export_something one, because it would need an additional @export annotation to properly work. Maybe I'm worrying too much and custom annotations will easily be able to provide all the needed functionality. |
so this is what I had in mind for reducing the amount of annotation per line...
could become (could as in optional)
|
I know very little about annotations in other languages but what's being discussed here looks like a fun addition to GDScript. Looking forward to it. |
It's already in my proposal that you'll be able to do this (doesn't matter if annotations have arguments or not):
|
There's not a proposal for custom annotations yet. I don't really see anything in this one that would hinder custom annotations in the future, but before I go that route I need to see how people are going to use custom annotations, and for that I need an actual proposal. Otherwise I might add stuff that nobody uses and miss something that many people expect. If custom annotations are able to edit the PropertyInfo, then they can change the AFAIK there's nothing in PropertyInfo that allows a custom editor, so this needs to be done before we allow annotations to do it (well, can be at the same time, but it's an idea on its own). This can be part of the custom annotations proposal. |
@vnen
Yes, that's what i meant by "provide all the needed functionality"
And this is what I was talking about when I mentioned that a refactor of PropertyInfo could be required/preferred as I personally don't like the fact that built-in hints are hardcoded as an enum and custom ones are completely excluded from this system and would need an additional one. We'll talk about it in deep in the future. Talking about this proposal, how would I inspect the annotations assigned to a property/method/class? Is a system to get the property-bound, method-bound, class-bound etc. metadata expected to be added? |
This is also not included here. For much of the same reason: I don't know yet how people are gonna use it. I'm not against it, but I want to gather real use cases before committing to it. If nobody opens a proposal for custom annotations, I'll make one once I have time and ideas in mind. |
I think this looks great, and I love @willnationsdev idea to have annotations to hook up signals, but I'm not sure if I am understanding him correctly. I'm thinking something like this would connect some_function to my_area's body_entered signal:
For The other thing I was thinking that would be nice is if there was a way to avoid explicitly coding the subsequent Like maybe there could be export_node instead of export_node_path and export_node sets the value of the get_node call to the variable instead of just setting the node path? so this syntax:
would now make my_main_menu be typed to Control. At runtime my_main_menu would be the Control referred to by the node path linked in the editor - not just the NodePath to the Control. |
@jknightdoeswork The example signal connection annotation I gave was having the annotation be placed on an onready Node property rather than on a method. So, yeah, it could conceivably be done either way. @onready
@connect("body_entered", self, "some_function")
var area := get_node("Area2D") as Area2D
# Implied to connect signal onready? onready annotation doesn't make sense for a method.
@connect(area, "body_entered")
func some_function():
pass The latter syntax, if onready is always assumed, is arguably more concise. It also makes cleanup easier if you later choose to remove the callback since you only have to update the code in one place. I also agree that, in addition to having the For example, if the node is a descendant, then you can push initialization till You could also make it more transparent and just create Node configuration warnings in the editor if people don't add an Edit: Also, for generic configuration warnings not associated with a particular script implementation, see #922 which I just opened recently. |
I just wanted to point out that a syntax like that @connect(area, "body_entered")
func some_function():
pass would allow for signals connected via GUI to actually have their |
I have a few suggestions and questions: Annotation blocksInstead of writing this
we could write this
This would clean up a lot of variable declarations early on in a script and encourage grouping of variables/functions/etc. @hint_* annotationsInstead of having many different @export_* annotations, I suggest splitting them into separate @hint_* annotations.
becomes
This separates the concepts of exporting variables and export hints and would also act as a work around to an issue with my annotation block suggestion.
@Version annotationI have previously suggested in issue #26649 for the addition of a
Would this be possible? Would it result in a name conflict because both variables have the same name? It shouldn't if this were done at parse time because one or both of the lines would be skipped. Anyway, those are my ideas. What do you think? |
I like the idea of being able to have +1 for the version / feature flags idea. That'd definitely be cool to have. I can already think of a game I worked on where that would've been useful since we had stuff that required different logic depending on whether you were on Windows versus Linux (exactly like your circumstance, except it wasn't about file paths). |
I think |
File paths were probably a bad example. In fact any variable like that is probably a bad example as they can be set in the project settings. My real world example where I agree, |
@dalexeev the first option does not give me auto completion on body for Player and the second option is just as verbose (but less readable) than what I had written above how it currently looks in GDScript. I get that GDScript is not C/C#, but that's no reason not to improve it's workflow. 😕 |
that.... is interesting. 🤔 (Still, the variable is called |
@winteraa Perhaps this is because the condition contains an additional check. In the case of
I meant the |
Maybe something like this should be allowed in GDScript.
|
I'm pretty sure this is not on-topic on this proposal. Maybe move the discussion to a new proposal? |
Oh ups. For some reason I was certain this is kind of a general GDScript 2.0 thread. Apologies. |
For anyone who would like to test out the new annotation system, it has been implemented in this PR and is now available in the current master branch. You can either compile the engine to test it, or you can use iFire's Stern Flowers build. Important note: The master branch is still unstable and has many bugs, so don't try to open any existing projects with it. |
This is already implemented in godotengine/godot#40598 so it can be closed now. Any suggestion for improvement/change should be done as a new proposal. |
it's probably way too late for that, though I want to mention it none the less: has it ever been considered to use I think there's a couple of downsides in terms of ambiguity to the and lastly, I think expose just makes more sense in describing what the feature does. just a thought. |
It's not all that the keyword does, though. It also makes that field serializable when you save resources using that script. That's not just scenes, that's also resources. And you can expose a property to the inspector without making it serializable too, but not with the keyword. So whether the |
What happened with these though? Was it implemented in some way? |
Not yet, it is tracked in #1255 |
Describe the project you are working on:
The GDScript compiler.
Describe the problem or limitation you are having in your project:
Excessive keywords and complex syntax for some features (
export
,setget
). Some keywords are allowed as identifiers to overcome this, but it ends up being a bit hacky.Describe the feature / enhancement and how it helps to overcome the problem or limitation:
Adding annotation syntax to GDScript to offload the amount of reserved words. With the potential possibility of including meta-data to scripts so first- and third-party tools can use them to provide extra features.
Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
This follows up from my initial idea on godotengine/godot#20318.
Annotations are used to extend a GDScript with functionality that is not directly tied to behavior. That includes editor attributes, such as
tool
andexport
, networking tags likemaster
andpuppet
, tweaking warning reporting (instead of comments), and other small things like delayed initialization (onready
).Note:
setget
will be removed in favor of properties, so this won't be moved to an annotation. I'll open a different proposal for what I have in mind.Annotation syntax
Annotations start with an
@
(at sign) followed by an identifier that represents the annotation, e.g.@tool
.For the annotations that have parameters, you can send the arguments inside parenthesis like a function call:
Optionally, you can name the arguments to make it clear if needed:
Annotations can be on the same line as the affected statement (order doesn't matter):
There are only script level and class level annotations. So they can't go inside a function. Invalid annotations will be reported as errors, to prevent subtle bugs from typos.
I'm not sure yet how to provide a system for user-defined annotations, this can be part of a different proposal or extended from this one.
Export hints
We discussed this at the Godot Sprint in January and the winning idea is that each export kind has its own annotation name. This will potentially simplify completion and hints. Types will be retrieved from GDScript typing system to avoid redundancy.
A few examples:
I'll try to come up with an exhaustive list in the next days.
Also, we can leverage this to implement sections:
Completion
Code completion should list possible annotations after type the initial
@
. For annotations that require arguments, it should show tooltips with the expect arguments and potentially list valid values (e.g. valid warnings to enable/disable).This also includes highlighting for recognized annotations.
If this enhancement will not be used often, can it be worked around with a few lines of script?:
No, this will be used often and cannot be worked around.
Is there a reason why this should be core and not an add-on in the asset library?:
It's intrinsic to the GDScript implementation, cannot be provided as an addon.
The text was updated successfully, but these errors were encountered: