Skip to content

Commit d1134d3

Browse files
committed
Add user annotations
1 parent f0f5319 commit d1134d3

25 files changed

+1180
-73
lines changed

editor/code_editor.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1956,6 +1956,7 @@ CodeTextEditor::CodeTextEditor() {
19561956
cs.push_back("=");
19571957
cs.push_back("$");
19581958
cs.push_back("@");
1959+
cs.push_back("@@");
19591960
cs.push_back("\"");
19601961
cs.push_back("\'");
19611962
text_editor->set_code_completion_prefixes(cs);

modules/gdscript/config.py

+5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ def get_doc_classes():
1111
return [
1212
"@GDScript",
1313
"GDScript",
14+
"GDScriptAnnotation",
15+
"GDScriptClassAnnotation",
16+
"GDScriptFunctionAnnotation",
17+
"GDScriptSignalAnnotation",
1418
"GDScriptSyntaxHighlighter",
19+
"GDScriptVariableAnnotation",
1520
]
1621

1722

modules/gdscript/doc_classes/GDScript.xml

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@
1212
<link title="GDScript documentation index">$DOCS_URL/tutorials/scripting/gdscript/index.html</link>
1313
</tutorials>
1414
<methods>
15+
<method name="get_class_annotations" qualifiers="const">
16+
<return type="Array" />
17+
<description>
18+
Returns all [GDScriptAnnotation]s targeting the top-level class of this script.
19+
</description>
20+
</method>
21+
<method name="get_member_annotations" qualifiers="const">
22+
<return type="Array" />
23+
<param index="0" name="member" type="StringName" />
24+
<description>
25+
Returns all [GDScriptAnnotation]s targeting [param member].
26+
</description>
27+
</method>
28+
<method name="get_members_with_annotations" qualifiers="const">
29+
<return type="PackedStringArray" />
30+
<description>
31+
Returns all members of this script that is targeted by at least one [GDScriptAnnotation].
32+
</description>
33+
</method>
1534
<method name="new" qualifiers="vararg">
1635
<return type="Variant" />
1736
<description>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="GDScriptAnnotation" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
3+
<brief_description>
4+
A user annotation implemented in the GDScript language.
5+
</brief_description>
6+
<description>
7+
A user annotation implemented in the GDScript language. User annotations are metadata that can be associated with a GDScript.
8+
You can create a new GDScriptAnnotation by extending GDScriptAnnotation's subclasses, such as [GDScriptVariableAnnotation] and [GDScriptFunctionAnnotation].
9+
User annotations must have a class name, cannot be a nested class, and must define the constructor [code]_init[/code], which defines the annotation's parameters.
10+
</description>
11+
<tutorials>
12+
</tutorials>
13+
<methods>
14+
<method name="get_name" qualifiers="const">
15+
<return type="StringName" />
16+
<description>
17+
Returns the annotation's name, which is equal to the class name.
18+
</description>
19+
</method>
20+
</methods>
21+
<members>
22+
<member name="error_message" type="String" setter="set_error_message" getter="get_error_message">
23+
The annotation's current error message. Setting this to a non-empty value when the annotation is being compiled will cause the GDScript parser to emit an error.
24+
</member>
25+
</members>
26+
</class>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="GDScriptClassAnnotation" inherits="GDScriptAnnotation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
3+
<brief_description>
4+
A user annotation that can only target classes.
5+
</brief_description>
6+
<description>
7+
A user annotation that can only target classes.
8+
</description>
9+
<tutorials>
10+
</tutorials>
11+
<methods>
12+
<method name="_analyze" qualifiers="virtual">
13+
<return type="void" />
14+
<param index="0" name="name" type="StringName" />
15+
<description>
16+
Override to access the targeted class's information.
17+
- [param name] is the class's name.
18+
</description>
19+
</method>
20+
<method name="_get_allow_multiple" qualifiers="virtual const">
21+
<return type="bool" />
22+
<description>
23+
Override to specify if there can be multiple instances of this annotation on the same target. If not overridden, the default is [code]false[/code].
24+
</description>
25+
</method>
26+
</methods>
27+
</class>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="GDScriptFunctionAnnotation" inherits="GDScriptAnnotation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
3+
<brief_description>
4+
A user annotation that can only target functions.
5+
</brief_description>
6+
<description>
7+
A user annotation that can only target functions.
8+
</description>
9+
<tutorials>
10+
</tutorials>
11+
<methods>
12+
<method name="_analyze" qualifiers="virtual">
13+
<return type="void" />
14+
<param index="0" name="name" type="StringName" />
15+
<param index="1" name="parameter_names" type="PackedStringArray" />
16+
<param index="2" name="parameter_type_names" type="PackedStringArray" />
17+
<param index="3" name="parameter_builtin_types" type="PackedInt32Array" />
18+
<param index="4" name="return_type_name" type="StringName" />
19+
<param index="5" name="return_builtin_type" type="int" enum="Variant.Type" />
20+
<param index="6" name="default_arguments" type="Array" />
21+
<param index="7" name="is_static" type="bool" />
22+
<param index="8" name="is_coroutine" type="bool" />
23+
<description>
24+
Override to access the targeted function's information. This can be used for validation, for example requiring the function to have a specific signature.
25+
- [param name] is the function's name;
26+
- [param parameter_names] is an array containing every parameter's name.
27+
- [param parameter_type_names] is an array containing every parameter's type name.
28+
- [param parameter_builtin_types] is an array containing every parameter's type, as an [int] (see [enum Variant.Type]);
29+
- [param return_type_name] is the return type's type name;
30+
- [param return_builtin_type] is the return type, as an [int] (see [enum Variant.Type]);
31+
- [param default_arguments] is an array containing default arguments for the function;
32+
- [param is_static] is whether the function is static or not;
33+
- [param is_coroutine] is whether the function is a coroutine or not.
34+
</description>
35+
</method>
36+
<method name="_get_allow_multiple" qualifiers="virtual const">
37+
<return type="bool" />
38+
<description>
39+
Override to specify if there can be multiple instances of this annotation on the same target. If not overridden, the default is [code]false[/code].
40+
</description>
41+
</method>
42+
</methods>
43+
</class>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="GDScriptSignalAnnotation" inherits="GDScriptAnnotation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
3+
<brief_description>
4+
A user annotation that can only target signals.
5+
</brief_description>
6+
<description>
7+
A user annotation that can only target signals.
8+
</description>
9+
<tutorials>
10+
</tutorials>
11+
<methods>
12+
<method name="_analyze" qualifiers="virtual">
13+
<return type="void" />
14+
<param index="0" name="name" type="StringName" />
15+
<param index="1" name="parameter_names" type="PackedStringArray" />
16+
<param index="2" name="parameter_type_names" type="PackedStringArray" />
17+
<param index="3" name="parameter_builtin_types" type="PackedInt32Array" />
18+
<description>
19+
Override to access the targeted signal's information. This can be used for validation, for example requiring the signal to have a specific signature.
20+
- [param name] is the signal's name;
21+
- [param parameter_names] is an array containing every parameter's name.
22+
- [param parameter_type_names] is an array containing every parameter's type name.
23+
- [param parameter_builtin_types] is an array containing every parameter's type, as an [int] (see [enum Variant.Type]).
24+
</description>
25+
</method>
26+
<method name="_get_allow_multiple" qualifiers="virtual const">
27+
<return type="bool" />
28+
<description>
29+
Override to specify if there can be multiple instances of this annotation on the same target. If not overridden, the default is [code]false[/code].
30+
</description>
31+
</method>
32+
</methods>
33+
</class>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<class name="GDScriptVariableAnnotation" inherits="GDScriptAnnotation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
3+
<brief_description>
4+
A user annotation that can only target variables.
5+
</brief_description>
6+
<description>
7+
A user annotation that can only target variables. By overriding [method _is_export_annotation], this annotation can also act as an export annotation like [annotation @GDScript.@export_custom].
8+
</description>
9+
<tutorials>
10+
</tutorials>
11+
<methods>
12+
<method name="_analyze" qualifiers="virtual">
13+
<return type="void" />
14+
<param index="0" name="name" type="StringName" />
15+
<param index="1" name="type_name" type="StringName" />
16+
<param index="2" name="type" type="int" enum="Variant.Type" />
17+
<param index="3" name="is_static" type="bool" />
18+
<description>
19+
Override to access the targeted variable's information. This can be used for validation, for example requiring the variable to be a certain type.
20+
- [param name] is the variable's name;
21+
- [param type_name] is the variable's type name;
22+
- [param type] is the variable's type, as an [int] (see [enum Variant.Type]);
23+
- [param is_static] is whether the variable is static or not.
24+
</description>
25+
</method>
26+
<method name="_get_allow_multiple" qualifiers="virtual const">
27+
<return type="bool" />
28+
<description>
29+
Override to specify if there can be multiple instances of this annotation on the same target. If not overridden, the default is [code]false[/code].
30+
</description>
31+
</method>
32+
<method name="_get_property_hint" qualifiers="virtual const">
33+
<return type="int" />
34+
<description>
35+
Override to specify the annotation's property hint. If not overridden, the default is [constant PROPERTY_HINT_NONE].
36+
</description>
37+
</method>
38+
<method name="_get_property_hint_string" qualifiers="virtual const">
39+
<return type="String" />
40+
<description>
41+
Override to specify the annotation's property hint string. If not overridden, the default is an empty string.
42+
</description>
43+
</method>
44+
<method name="_get_property_usage" qualifiers="virtual const">
45+
<return type="int" />
46+
<description>
47+
Override to specify the annotation's property usage. If not overridden, the default is [constant PROPERTY_USAGE_DEFAULT].
48+
</description>
49+
</method>
50+
<method name="_is_export_annotation" qualifiers="virtual const">
51+
<return type="bool" />
52+
<description>
53+
Override to specify if the annotation should be treated as an export annotation. If not overridden, the default is [code]false[/code].
54+
</description>
55+
</method>
56+
</methods>
57+
</class>

modules/gdscript/editor/gdscript_highlighter.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
5757
bool in_node_path = false;
5858
bool in_node_ref = false;
5959
bool in_annotation = false;
60+
bool in_user_annotation = false;
6061
bool in_string_name = false;
6162
bool is_hex_notation = false;
6263
bool is_bin_notation = false;
@@ -593,8 +594,11 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
593594

594595
if (!in_annotation && in_region == -1 && str[j] == '@') {
595596
in_annotation = true;
597+
} else if (in_annotation && in_region == -1 && str[j] == '@') {
598+
in_user_annotation = true;
596599
} else if (in_region != -1 || is_a_symbol) {
597600
in_annotation = false;
601+
in_user_annotation = false;
598602
}
599603

600604
const bool in_raw_string_prefix = in_region == -1 && str[j] == 'r' && j + 1 < line_length && (str[j + 1] == '"' || str[j + 1] == '\'');
@@ -604,7 +608,7 @@ Dictionary GDScriptSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_l
604608
} else if (in_node_ref) {
605609
next_type = NODE_REF;
606610
color = node_ref_color;
607-
} else if (in_annotation) {
611+
} else if (in_annotation || in_user_annotation) {
608612
next_type = ANNOTATION;
609613
color = annotation_color;
610614
} else if (in_string_name) {

modules/gdscript/gdscript.cpp

+27-4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "gdscript.h"
3232

3333
#include "gdscript_analyzer.h"
34+
#include "gdscript_annotation.h"
3435
#include "gdscript_cache.h"
3536
#include "gdscript_compiler.h"
3637
#include "gdscript_parser.h"
@@ -150,7 +151,7 @@ void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance
150151
}
151152
}
152153

153-
GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) {
154+
GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error, bool p_show_error) {
154155
/* STEP 1, CREATE */
155156

156157
GDScriptInstance *instance = memnew(GDScriptInstance);
@@ -182,7 +183,9 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
182183
MutexLock lock(GDScriptLanguage::singleton->mutex);
183184
instances.erase(p_owner);
184185
}
185-
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance: " + error_text);
186+
if (p_show_error) {
187+
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance: " + error_text);
188+
}
186189
}
187190

188191
if (p_argcount < 0) {
@@ -200,14 +203,20 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco
200203
MutexLock lock(GDScriptLanguage::singleton->mutex);
201204
instances.erase(p_owner);
202205
}
203-
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance: " + error_text);
206+
if (p_show_error) {
207+
ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance: " + error_text);
208+
}
204209
}
205210
}
206211
//@TODO make thread safe
207212
return instance;
208213
}
209214

210215
Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
216+
return _new_internal(p_args, p_argcount, r_error, true);
217+
}
218+
219+
Variant GDScript::_new_internal(const Variant **p_args, int p_argcount, Callable::CallError &r_error, bool p_show_error) {
211220
/* STEP 1, CREATE */
212221

213222
if (!valid) {
@@ -237,7 +246,7 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr
237246
ref = Ref<RefCounted>(r);
238247
}
239248

240-
GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error);
249+
GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error, p_show_error);
241250
if (!instance) {
242251
if (ref.is_null()) {
243252
memdelete(owner); //no owner, sorry
@@ -581,6 +590,11 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
581590
break; // Nothing.
582591
}
583592
}
593+
594+
member_annotations.clear();
595+
for (int i = 0; i < parser.get_member_annotations().size(); i++) {
596+
member_annotations[parser.get_member_annotations()[i].first] = parser.get_member_annotations()[i].second;
597+
}
584598
} else {
585599
placeholder_fallback_enabled = true;
586600
return false;
@@ -850,6 +864,12 @@ Error GDScript::reload(bool p_keep_state) {
850864

851865
can_run = ScriptServer::is_scripting_enabled() || parser.is_tool();
852866

867+
class_annotations = parser.get_class_annotations();
868+
member_annotations.clear();
869+
for (int i = 0; i < parser.get_member_annotations().size(); i++) {
870+
member_annotations[parser.get_member_annotations()[i].first] = parser.get_member_annotations()[i].second;
871+
}
872+
853873
GDScriptCompiler compiler;
854874
err = compiler.compile(&parser, this, p_keep_state);
855875

@@ -1073,6 +1093,9 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
10731093

10741094
void GDScript::_bind_methods() {
10751095
ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &GDScript::_new, MethodInfo("new"));
1096+
ClassDB::bind_method(D_METHOD("get_members_with_annotations"), &GDScript::get_members_with_annotations);
1097+
ClassDB::bind_method(D_METHOD("get_member_annotations", "member"), &GDScript::get_member_annotations);
1098+
ClassDB::bind_method(D_METHOD("get_class_annotations"), &GDScript::get_class_annotations);
10761099
}
10771100

10781101
void GDScript::set_path(const String &p_path, bool p_take_over) {

0 commit comments

Comments
 (0)