Add support for documenting built-in annotations

This commit is contained in:
Yuri Sizov 2022-07-04 18:56:34 +03:00
parent b4644e2835
commit a9098e6147
19 changed files with 462 additions and 17 deletions

View File

@ -163,6 +163,7 @@ public:
Vector<ConstantDoc> constants; Vector<ConstantDoc> constants;
HashMap<String, String> enums; HashMap<String, String> enums;
Vector<PropertyDoc> properties; Vector<PropertyDoc> properties;
Vector<MethodDoc> annotations;
Vector<ThemeItemDoc> theme_properties; Vector<ThemeItemDoc> theme_properties;
bool is_script_doc = false; bool is_script_doc = false;
String script_path; String script_path;

View File

@ -350,6 +350,7 @@ public:
LOOKUP_RESULT_CLASS_SIGNAL, LOOKUP_RESULT_CLASS_SIGNAL,
LOOKUP_RESULT_CLASS_ENUM, LOOKUP_RESULT_CLASS_ENUM,
LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE, LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE,
LOOKUP_RESULT_CLASS_ANNOTATION,
LOOKUP_RESULT_MAX LOOKUP_RESULT_MAX
}; };
@ -402,6 +403,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const = 0; virtual void get_recognized_extensions(List<String> *p_extensions) const = 0;
virtual void get_public_functions(List<MethodInfo> *p_functions) const = 0; virtual void get_public_functions(List<MethodInfo> *p_functions) const = 0;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const = 0; virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const = 0;
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const = 0;
struct ProfilingInfo { struct ProfilingInfo {
StringName signature; StringName signature;

View File

@ -134,6 +134,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_recognized_extensions); GDVIRTUAL_BIND(_get_recognized_extensions);
GDVIRTUAL_BIND(_get_public_functions); GDVIRTUAL_BIND(_get_public_functions);
GDVIRTUAL_BIND(_get_public_constants); GDVIRTUAL_BIND(_get_public_constants);
GDVIRTUAL_BIND(_get_public_annotations);
GDVIRTUAL_BIND(_profiling_start); GDVIRTUAL_BIND(_profiling_start);
GDVIRTUAL_BIND(_profiling_stop); GDVIRTUAL_BIND(_profiling_stop);
@ -160,6 +161,7 @@ void ScriptLanguageExtension::_bind_methods() {
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_SIGNAL); BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_SIGNAL);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ENUM); BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ENUM);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE); BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ANNOTATION);
BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX); BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX);
BIND_ENUM_CONSTANT(LOCATION_LOCAL); BIND_ENUM_CONSTANT(LOCATION_LOCAL);

View File

@ -580,6 +580,15 @@ public:
p_constants->push_back(Pair<String, Variant>(d["name"], d["value"])); p_constants->push_back(Pair<String, Variant>(d["name"], d["value"]));
} }
} }
GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations)
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
for (int i = 0; i < ret.size(); i++) {
MethodInfo mi = MethodInfo::from_dict(ret[i]);
p_annotations->push_back(mi);
}
}
EXBIND0(profiling_start) EXBIND0(profiling_start)
EXBIND0(profiling_stop) EXBIND0(profiling_stop)

View File

@ -162,6 +162,42 @@
</xs:sequence> </xs:sequence>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>
<xs:element name="annotations" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="annotation" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="return" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:sequence />
</xs:sequence>
<xs:attribute type="xs:string" name="type" />
<xs:attribute type="xs:string" name="enum" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="argument" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:sequence />
</xs:sequence>
<xs:attribute type="xs:byte" name="index" />
<xs:attribute type="xs:string" name="name" />
<xs:attribute type="xs:string" name="type" />
<xs:attribute type="xs:string" name="enum" use="optional" />
<xs:attribute type="xs:string" name="default" use="optional" />
</xs:complexType>
</xs:element>
<xs:element type="xs:string" name="description" />
</xs:sequence>
<xs:attribute type="xs:string" name="name" use="optional" />
<xs:attribute type="xs:string" name="qualifiers" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="theme_items" minOccurs="0"> <xs:element name="theme_items" minOccurs="0">
<xs:complexType> <xs:complexType>
<xs:sequence> <xs:sequence>

View File

@ -174,6 +174,11 @@
<description> <description>
</description> </description>
</method> </method>
<method name="_get_public_annotations" qualifiers="virtual const">
<return type="Dictionary[]" />
<description>
</description>
</method>
<method name="_get_public_constants" qualifiers="virtual const"> <method name="_get_public_constants" qualifiers="virtual const">
<return type="Dictionary" /> <return type="Dictionary" />
<description> <description>
@ -378,7 +383,9 @@
</constant> </constant>
<constant name="LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE" value="7" enum="LookupResultType"> <constant name="LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE" value="7" enum="LookupResultType">
</constant> </constant>
<constant name="LOOKUP_RESULT_MAX" value="8" enum="LookupResultType"> <constant name="LOOKUP_RESULT_CLASS_ANNOTATION" value="8" enum="LookupResultType">
</constant>
<constant name="LOOKUP_RESULT_MAX" value="9" enum="LookupResultType">
</constant> </constant>
<constant name="LOCATION_LOCAL" value="0" enum="CodeCompletionLocation"> <constant name="LOCATION_LOCAL" value="0" enum="CodeCompletionLocation">
The option is local to the location of the code completion query - e.g. a local variable. The option is local to the location of the code completion query - e.g. a local variable.

View File

@ -36,6 +36,7 @@ BASE_STRINGS = [
"Signals", "Signals",
"Enumerations", "Enumerations",
"Constants", "Constants",
"Annotations",
"Property Descriptions", "Property Descriptions",
"Constructor Descriptions", "Constructor Descriptions",
"Method Descriptions", "Method Descriptions",
@ -155,6 +156,7 @@ class ClassDef:
self.methods = OrderedDict() # type: OrderedDict[str, List[MethodDef]] self.methods = OrderedDict() # type: OrderedDict[str, List[MethodDef]]
self.operators = OrderedDict() # type: OrderedDict[str, List[MethodDef]] self.operators = OrderedDict() # type: OrderedDict[str, List[MethodDef]]
self.signals = OrderedDict() # type: OrderedDict[str, SignalDef] self.signals = OrderedDict() # type: OrderedDict[str, SignalDef]
self.annotations = OrderedDict() # type: OrderedDict[str, List[MethodDef]]
self.theme_items = OrderedDict() # type: OrderedDict[str, ThemeItemDef] self.theme_items = OrderedDict() # type: OrderedDict[str, ThemeItemDef]
self.inherits = None # type: Optional[str] self.inherits = None # type: Optional[str]
self.brief_description = None # type: Optional[str] self.brief_description = None # type: Optional[str]
@ -323,6 +325,27 @@ class State:
enum_def.values[constant_name] = constant_def enum_def.values[constant_name] = constant_def
annotations = class_root.find("annotations")
if annotations is not None:
for annotation in annotations:
assert annotation.tag == "annotation"
annotation_name = annotation.attrib["name"]
qualifiers = annotation.get("qualifiers")
params = parse_arguments(annotation)
desc_element = annotation.find("description")
annotation_desc = None
if desc_element is not None:
annotation_desc = desc_element.text
annotation_def = MethodDef(annotation_name, return_type, params, annotation_desc, qualifiers)
if annotation_name not in class_def.annotations:
class_def.annotations[annotation_name] = []
class_def.annotations[annotation_name].append(annotation_def)
signals = class_root.find("signals") signals = class_root.find("signals")
if signals is not None: if signals is not None:
for signal in signals: for signal in signals:
@ -732,6 +755,26 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S
f.write("\n\n") f.write("\n\n")
if len(class_def.annotations) > 0:
f.write(make_heading("Annotations", "-"))
index = 0
for method_list in class_def.annotations.values():
for i, m in enumerate(method_list):
if index != 0:
f.write("----\n\n")
if i == 0:
f.write(".. _class_{}_annotation_{}:\n\n".format(class_name, m.name.strip("@")))
ret_type, signature = make_method_signature(class_def, m, "", state)
f.write("- {} {}\n\n".format(ret_type, signature))
if m.description is not None and m.description.strip() != "":
f.write(rstize_text(m.description.strip(), state) + "\n\n")
index += 1
# Property descriptions # Property descriptions
if any(not p.overrides for p in class_def.properties.values()) > 0: if any(not p.overrides for p in class_def.properties.values()) > 0:
f.write(make_heading("Property Descriptions", "-")) f.write(make_heading("Property Descriptions", "-"))
@ -1065,6 +1108,11 @@ def rstize_text(text, state): # type: (str, State) -> str
print_error('{}.xml: Unresolved signal "{}".'.format(state.current_class, param), state) print_error('{}.xml: Unresolved signal "{}".'.format(state.current_class, param), state)
ref_type = "_signal" ref_type = "_signal"
elif cmd.startswith("annotation"):
if method_param not in class_def.annotations:
print_error('{}.xml: Unresolved annotation "{}".'.format(state.current_class, param), state)
ref_type = "_annotation"
elif cmd.startswith("constant"): elif cmd.startswith("constant"):
found = False found = False

View File

@ -178,6 +178,20 @@ void DocTools::merge_from(const DocTools &p_data) {
} }
} }
for (int i = 0; i < c.annotations.size(); i++) {
DocData::MethodDoc &m = c.annotations.write[i];
for (int j = 0; j < cf.annotations.size(); j++) {
if (cf.annotations[j].name != m.name) {
continue;
}
const DocData::MethodDoc &mf = cf.annotations[j];
m.description = mf.description;
break;
}
}
for (int i = 0; i < c.properties.size(); i++) { for (int i = 0; i < c.properties.size(); i++) {
DocData::PropertyDoc &p = c.properties.write[i]; DocData::PropertyDoc &p = c.properties.write[i];
@ -959,8 +973,41 @@ void DocTools::generate(bool p_basic_types) {
c.constants.push_back(cd); c.constants.push_back(cd);
} }
// Get annotations.
List<MethodInfo> ainfo;
lang->get_public_annotations(&ainfo);
for (const MethodInfo &ai : ainfo) {
DocData::MethodDoc atd;
atd.name = ai.name;
if (ai.flags & METHOD_FLAG_VARARG) {
if (!atd.qualifiers.is_empty()) {
atd.qualifiers += " ";
}
atd.qualifiers += "vararg";
}
DocData::return_doc_from_retinfo(atd, ai.return_val);
for (int j = 0; j < ai.arguments.size(); j++) {
DocData::ArgumentDoc ad;
DocData::argument_doc_from_arginfo(ad, ai.arguments[j]);
int darg_idx = j - (ai.arguments.size() - ai.default_arguments.size());
if (darg_idx >= 0) {
Variant default_arg = ai.default_arguments[darg_idx];
ad.default_value = default_arg.get_construct_string().replace("\n", " ");
}
atd.arguments.push_back(ad);
}
c.annotations.push_back(atd);
}
// Skip adding the lang if it doesn't expose anything (e.g. C#). // Skip adding the lang if it doesn't expose anything (e.g. C#).
if (c.methods.is_empty() && c.constants.is_empty()) { if (c.methods.is_empty() && c.constants.is_empty() && c.annotations.is_empty()) {
continue; continue;
} }
@ -1162,6 +1209,9 @@ Error DocTools::_load(Ref<XMLParser> parser) {
} else if (name2 == "signals") { } else if (name2 == "signals") {
Error err2 = _parse_methods(parser, c.signals); Error err2 = _parse_methods(parser, c.signals);
ERR_FAIL_COND_V(err2, err2); ERR_FAIL_COND_V(err2, err2);
} else if (name2 == "annotations") {
Error err2 = _parse_methods(parser, c.annotations);
ERR_FAIL_COND_V(err2, err2);
} else if (name2 == "members") { } else if (name2 == "members") {
while (parser->read() == OK) { while (parser->read() == OK) {
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
@ -1442,6 +1492,8 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
_write_string(f, 1, "</constants>"); _write_string(f, 1, "</constants>");
} }
_write_method_doc(f, "annotation", c.annotations);
if (!c.theme_properties.is_empty()) { if (!c.theme_properties.is_empty()) {
c.theme_properties.sort(); c.theme_properties.sort();

View File

@ -125,6 +125,9 @@ void EditorHelp::_class_desc_select(const String &p_select) {
} else if (tag == "constant") { } else if (tag == "constant") {
topic = "class_constant"; topic = "class_constant";
table = &this->constant_line; table = &this->constant_line;
} else if (tag == "annotation") {
topic = "class_annotation";
table = &this->annotation_line;
} else if (tag == "theme_item") { } else if (tag == "theme_item") {
topic = "theme_item"; topic = "theme_item";
table = &this->theme_property_line; table = &this->theme_property_line;
@ -274,7 +277,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
class_desc->add_text(" "); class_desc->add_text(" ");
} }
if (p_overview && !p_method.description.is_empty()) { if (p_overview && !p_method.description.strip_edges().is_empty()) {
class_desc->push_meta("@method " + p_method.name); class_desc->push_meta("@method " + p_method.name);
} }
@ -282,7 +285,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
_add_text(p_method.name); _add_text(p_method.name);
class_desc->pop(); class_desc->pop();
if (p_overview && !p_method.description.is_empty()) { if (p_overview && !p_method.description.strip_edges().is_empty()) {
class_desc->pop(); //meta class_desc->pop(); //meta
} }
@ -412,7 +415,7 @@ void EditorHelp::_update_method_list(const Vector<DocData::MethodDoc> p_methods,
class_desc->pop(); //cell class_desc->pop(); //cell
} }
if (!m[i].description.is_empty() || m[i].errors_returned.size() > 0) { if (!m[i].description.strip_edges().is_empty() || m[i].errors_returned.size() > 0) {
r_method_descrpitons = true; r_method_descrpitons = true;
} }
@ -611,7 +614,7 @@ void EditorHelp::_update_doc() {
class_desc->add_newline(); class_desc->add_newline();
// Brief description // Brief description
if (!cd.brief_description.is_empty()) { if (!cd.brief_description.strip_edges().is_empty()) {
class_desc->push_color(text_color); class_desc->push_color(text_color);
class_desc->push_font(doc_bold_font); class_desc->push_font(doc_bold_font);
class_desc->push_indent(1); class_desc->push_indent(1);
@ -625,7 +628,7 @@ void EditorHelp::_update_doc() {
} }
// Class description // Class description
if (!cd.description.is_empty()) { if (!cd.description.strip_edges().is_empty()) {
section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_paragraph_count() - 2)); section_line.push_back(Pair<String, int>(TTR("Description"), class_desc->get_paragraph_count() - 2));
description_line = class_desc->get_paragraph_count() - 2; description_line = class_desc->get_paragraph_count() - 2;
class_desc->push_color(title_color); class_desc->push_color(title_color);
@ -692,7 +695,7 @@ void EditorHelp::_update_doc() {
if (cd.is_script_doc) { if (cd.is_script_doc) {
has_properties = false; has_properties = false;
for (int i = 0; i < cd.properties.size(); i++) { for (int i = 0; i < cd.properties.size(); i++) {
if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.is_empty()) { if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.strip_edges().is_empty()) {
continue; continue;
} }
has_properties = true; has_properties = true;
@ -718,7 +721,7 @@ void EditorHelp::_update_doc() {
for (int i = 0; i < cd.properties.size(); i++) { for (int i = 0; i < cd.properties.size(); i++) {
// Ignore undocumented private. // Ignore undocumented private.
if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.is_empty()) { if (cd.properties[i].name.begins_with("_") && cd.properties[i].description.strip_edges().is_empty()) {
continue; continue;
} }
property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2; //gets overridden if description property_line[cd.properties[i].name] = class_desc->get_paragraph_count() - 2; //gets overridden if description
@ -743,7 +746,7 @@ void EditorHelp::_update_doc() {
describe = true; describe = true;
} }
if (!cd.properties[i].description.is_empty()) { if (!cd.properties[i].description.strip_edges().is_empty()) {
describe = true; describe = true;
} }
@ -856,7 +859,7 @@ void EditorHelp::_update_doc() {
} }
} }
// Ignore undocumented non virtual private. // Ignore undocumented non virtual private.
if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.is_empty() && !cd.methods[i].qualifiers.contains("virtual")) { if (cd.methods[i].name.begins_with("_") && cd.methods[i].description.strip_edges().is_empty() && !cd.methods[i].qualifiers.contains("virtual")) {
continue; continue;
} }
methods.push_back(cd.methods[i]); methods.push_back(cd.methods[i]);
@ -976,7 +979,7 @@ void EditorHelp::_update_doc() {
class_desc->pop(); // monofont class_desc->pop(); // monofont
// Theme item description. // Theme item description.
if (!cd.theme_properties[i].description.is_empty()) { if (!cd.theme_properties[i].description.strip_edges().is_empty()) {
class_desc->push_font(doc_font); class_desc->push_font(doc_font);
class_desc->push_color(comment_color); class_desc->push_color(comment_color);
class_desc->push_indent(1); class_desc->push_indent(1);
@ -1018,8 +1021,8 @@ void EditorHelp::_update_doc() {
signal_line[cd.signals[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description. signal_line[cd.signals[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
class_desc->push_font(doc_code_font); // monofont class_desc->push_font(doc_code_font); // monofont
class_desc->push_color(headline_color);
_add_bulletpoint(); _add_bulletpoint();
class_desc->push_color(headline_color);
_add_text(cd.signals[i].name); _add_text(cd.signals[i].name);
class_desc->pop(); class_desc->pop();
class_desc->push_color(symbol_color); class_desc->push_color(symbol_color);
@ -1048,7 +1051,7 @@ void EditorHelp::_update_doc() {
class_desc->add_text(")"); class_desc->add_text(")");
class_desc->pop(); class_desc->pop();
class_desc->pop(); // end monofont class_desc->pop(); // end monofont
if (!cd.signals[i].description.is_empty()) { if (!cd.signals[i].description.strip_edges().is_empty()) {
class_desc->push_font(doc_font); class_desc->push_font(doc_font);
class_desc->push_color(comment_color); class_desc->push_color(comment_color);
class_desc->push_indent(1); class_desc->push_indent(1);
@ -1079,7 +1082,7 @@ void EditorHelp::_update_doc() {
enums[cd.constants[i].enumeration].push_back(cd.constants[i]); enums[cd.constants[i].enumeration].push_back(cd.constants[i]);
} else { } else {
// Ignore undocumented private. // Ignore undocumented private.
if (cd.constants[i].name.begins_with("_") && cd.constants[i].description.is_empty()) { if (cd.constants[i].name.begins_with("_") && cd.constants[i].description.strip_edges().is_empty()) {
continue; continue;
} }
constants.push_back(cd.constants[i]); constants.push_back(cd.constants[i]);
@ -1151,8 +1154,8 @@ void EditorHelp::_update_doc() {
constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2; constant_line[enum_list[i].name] = class_desc->get_paragraph_count() - 2;
class_desc->push_font(doc_code_font); class_desc->push_font(doc_code_font);
class_desc->push_color(headline_color);
_add_bulletpoint(); _add_bulletpoint();
class_desc->push_color(headline_color);
_add_text(enum_list[i].name); _add_text(enum_list[i].name);
class_desc->pop(); class_desc->pop();
class_desc->push_color(symbol_color); class_desc->push_color(symbol_color);
@ -1236,7 +1239,7 @@ void EditorHelp::_update_doc() {
class_desc->add_newline(); class_desc->add_newline();
if (!constants[i].description.is_empty()) { if (!constants[i].description.strip_edges().is_empty()) {
class_desc->push_font(doc_font); class_desc->push_font(doc_font);
class_desc->push_color(comment_color); class_desc->push_color(comment_color);
_add_text(DTR(constants[i].description)); _add_text(DTR(constants[i].description));
@ -1255,6 +1258,112 @@ void EditorHelp::_update_doc() {
} }
} }
// Annotations
if (!cd.annotations.is_empty()) {
if (sort_methods) {
cd.annotations.sort();
}
section_line.push_back(Pair<String, int>(TTR("Annotations"), class_desc->get_paragraph_count() - 2));
class_desc->push_color(title_color);
class_desc->push_font(doc_title_font);
class_desc->push_font_size(doc_title_font_size);
class_desc->add_text(TTR("Annotations"));
class_desc->pop(); // font size
class_desc->pop(); // font
class_desc->pop(); // color
class_desc->add_newline();
class_desc->add_newline();
class_desc->push_indent(1);
for (int i = 0; i < cd.annotations.size(); i++) {
annotation_line[cd.annotations[i].name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
class_desc->push_font(doc_code_font); // monofont
_add_bulletpoint();
class_desc->push_color(headline_color);
_add_text(cd.annotations[i].name);
class_desc->pop();
if (cd.annotations[i].arguments.size() > 0) {
class_desc->push_color(symbol_color);
class_desc->add_text("(");
class_desc->pop();
for (int j = 0; j < cd.annotations[i].arguments.size(); j++) {
class_desc->push_color(text_color);
if (j > 0) {
class_desc->add_text(", ");
}
_add_text(cd.annotations[i].arguments[j].name);
class_desc->add_text(": ");
_add_type(cd.annotations[i].arguments[j].type);
if (!cd.annotations[i].arguments[j].default_value.is_empty()) {
class_desc->push_color(symbol_color);
class_desc->add_text(" = ");
class_desc->pop();
_add_text(cd.annotations[i].arguments[j].default_value);
}
class_desc->pop();
}
if (cd.annotations[i].qualifiers.contains("vararg")) {
class_desc->push_color(text_color);
if (cd.annotations[i].arguments.size()) {
class_desc->add_text(", ");
}
class_desc->push_color(symbol_color);
class_desc->add_text("...");
class_desc->pop();
class_desc->pop();
}
class_desc->push_color(symbol_color);
class_desc->add_text(")");
class_desc->pop();
}
if (!cd.annotations[i].qualifiers.is_empty()) {
class_desc->push_color(qualifier_color);
class_desc->add_text(" ");
_add_text(cd.annotations[i].qualifiers);
class_desc->pop();
}
class_desc->pop(); // end monofont
if (!cd.annotations[i].description.strip_edges().is_empty()) {
class_desc->push_font(doc_font);
class_desc->push_color(comment_color);
class_desc->push_indent(1);
_add_text(DTR(cd.annotations[i].description));
class_desc->pop(); // indent
class_desc->pop();
class_desc->pop(); // font
} else {
class_desc->push_indent(1);
class_desc->add_image(get_theme_icon(SNAME("Error"), SNAME("EditorIcons")));
class_desc->add_text(" ");
class_desc->push_color(comment_color);
if (cd.is_script_doc) {
class_desc->append_text(TTR("There is currently no description for this annotation."));
} else {
class_desc->append_text(TTR("There is currently no description for this annotation. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
}
class_desc->pop();
class_desc->pop(); // indent
}
class_desc->add_newline();
class_desc->add_newline();
}
class_desc->pop();
class_desc->add_newline();
}
// Property descriptions // Property descriptions
if (property_descr) { if (property_descr) {
section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2)); section_line.push_back(Pair<String, int>(TTR("Property Descriptions"), class_desc->get_paragraph_count() - 2));
@ -1501,6 +1610,10 @@ void EditorHelp::_help_callback(const String &p_topic) {
if (constant_line.has(name)) { if (constant_line.has(name)) {
line = constant_line[name]; line = constant_line[name];
} }
} else if (what == "class_annotation") {
if (annotation_line.has(name)) {
line = annotation_line[name];
}
} else if (what == "class_global") { } else if (what == "class_global") {
if (constant_line.has(name)) { if (constant_line.has(name)) {
line = constant_line[name]; line = constant_line[name];

View File

@ -110,6 +110,7 @@ class EditorHelp : public VBoxContainer {
HashMap<String, int> property_line; HashMap<String, int> property_line;
HashMap<String, int> theme_property_line; HashMap<String, int> theme_property_line;
HashMap<String, int> constant_line; HashMap<String, int> constant_line;
HashMap<String, int> annotation_line;
HashMap<String, int> enum_line; HashMap<String, int> enum_line;
HashMap<String, HashMap<String, int>> enum_values_line; HashMap<String, HashMap<String, int>> enum_values_line;
int description_line = 0; int description_line = 0;

View File

@ -886,6 +886,9 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
emit_signal(SNAME("go_to_help"), "class_enum:" + result.class_name + ":" + result.class_member); emit_signal(SNAME("go_to_help"), "class_enum:" + result.class_name + ":" + result.class_member);
} break; } break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION: {
emit_signal(SNAME("go_to_help"), "class_annotation:" + result.class_name + ":" + result.class_member);
} break;
case ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE: { case ScriptLanguage::LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE: {
emit_signal(SNAME("go_to_help"), "class_global:" + result.class_name + ":" + result.class_member); emit_signal(SNAME("go_to_help"), "class_global:" + result.class_name + ":" + result.class_member);
} break; } break;

View File

@ -257,4 +257,145 @@
[b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead. [b]Note:[/b] "Not a Number" is only a concept with floating-point numbers, and has no equivalent for integers. Dividing an integer [code]0[/code] by [code]0[/code] will not result in [constant NAN] and will result in a run-time error instead.
</constant> </constant>
</constants> </constants>
<annotations>
<annotation name="@export">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_color_no_alpha">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_dir">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_enum" qualifiers="vararg">
<return type="void" />
<argument index="0" name="names" type="String" />
<description>
</description>
</annotation>
<annotation name="@export_exp_easing">
<return type="void" />
<argument index="0" name="hint1" type="String" default="null" />
<argument index="1" name="hint2" type="String" default="null" />
<description>
</description>
</annotation>
<annotation name="@export_file" qualifiers="vararg">
<return type="void" />
<argument index="0" name="filter" type="String" default="null" />
<description>
</description>
</annotation>
<annotation name="@export_flags" qualifiers="vararg">
<return type="void" />
<argument index="0" name="names" type="String" />
<description>
</description>
</annotation>
<annotation name="@export_flags_2d_navigation">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_flags_2d_physics">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_flags_2d_render">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_flags_3d_navigation">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_flags_3d_physics">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_flags_3d_render">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_global_dir">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_global_file" qualifiers="vararg">
<return type="void" />
<argument index="0" name="filter" type="String" default="null" />
<description>
</description>
</annotation>
<annotation name="@export_multiline">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_node_path" qualifiers="vararg">
<return type="void" />
<argument index="0" name="type" type="String" default="null" />
<description>
</description>
</annotation>
<annotation name="@export_placeholder">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@export_range">
<return type="void" />
<argument index="0" name="min" type="float" />
<argument index="1" name="max" type="float" />
<argument index="2" name="step" type="float" default="null" />
<argument index="3" name="slider1" type="String" default="null" />
<argument index="4" name="slider2" type="String" default="null" />
<argument index="5" name="slider3" type="String" default="null" />
<description>
</description>
</annotation>
<annotation name="@icon">
<return type="void" />
<argument index="0" name="icon_path" type="String" />
<description>
</description>
</annotation>
<annotation name="@onready">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@rpc" qualifiers="vararg">
<return type="void" />
<argument index="0" name="mode" type="String" default="null" />
<argument index="1" name="sync" type="String" default="null" />
<argument index="2" name="transfer_mode" type="String" default="null" />
<argument index="3" name="transfer_channel" type="int" default="null" />
<description>
</description>
</annotation>
<annotation name="@tool">
<return type="void" />
<description>
</description>
</annotation>
<annotation name="@warning_ignore" qualifiers="vararg">
<return type="void" />
<argument index="0" name="warning" type="String" />
<description>
</description>
</annotation>
</annotations>
</class> </class>

View File

@ -489,6 +489,7 @@ public:
virtual void get_public_functions(List<MethodInfo> *p_functions) const override; virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override; virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override;
virtual void profiling_start() override; virtual void profiling_start() override;
virtual void profiling_stop() override; virtual void profiling_stop() override;

View File

@ -445,6 +445,16 @@ void GDScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_const
p_constants->push_back(nan); p_constants->push_back(nan);
} }
void GDScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {
GDScriptParser parser;
List<MethodInfo> annotations;
parser.get_annotation_list(&annotations);
for (const MethodInfo &E : annotations) {
p_annotations->push_back(E);
}
}
String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const { String GDScriptLanguage::make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const {
#ifdef TOOLS_ENABLED #ifdef TOOLS_ENABLED
bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints"); bool th = EditorSettings::get_singleton()->get_setting("text_editor/completion/add_type_hints");
@ -3376,6 +3386,15 @@ static Error _lookup_symbol_from_base(const GDScriptParser::DataType &p_base, co
return OK; return OK;
} }
} break; } break;
case GDScriptParser::COMPLETION_ANNOTATION: {
const String annotation_symbol = "@" + p_symbol;
if (parser.annotation_exists(annotation_symbol)) {
r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_ANNOTATION;
r_result.class_name = "@GDScript";
r_result.class_member = annotation_symbol;
return OK;
}
} break;
default: { default: {
} }
} }

View File

@ -105,6 +105,10 @@ void GDScriptParser::get_annotation_list(List<MethodInfo> *r_annotations) const
} }
} }
bool GDScriptParser::annotation_exists(const String &p_annotation_name) const {
return valid_annotations.has(p_annotation_name);
}
GDScriptParser::GDScriptParser() { GDScriptParser::GDScriptParser() {
// Register valid annotations. // Register valid annotations.
// TODO: Should this be static? // TODO: Should this be static?

View File

@ -1413,6 +1413,7 @@ public:
CompletionContext get_completion_context() const { return completion_context; } CompletionContext get_completion_context() const { return completion_context; }
CompletionCall get_completion_call() const { return completion_call; } CompletionCall get_completion_call() const { return completion_call; }
void get_annotation_list(List<MethodInfo> *r_annotations) const; void get_annotation_list(List<MethodInfo> *r_annotations) const;
bool annotation_exists(const String &p_annotation_name) const;
const List<ParserError> &get_errors() const { return errors; } const List<ParserError> &get_errors() const { return errors; }
const List<String> get_dependencies() const { const List<String> get_dependencies() const {

View File

@ -502,6 +502,7 @@ public:
/* TODO? */ void get_public_functions(List<MethodInfo> *p_functions) const override {} /* TODO? */ void get_public_functions(List<MethodInfo> *p_functions) const override {}
/* TODO? */ void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {} /* TODO? */ void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {}
/* TODO? */ void get_public_annotations(List<MethodInfo> *p_annotations) const override {}
void reload_all_scripts() override; void reload_all_scripts() override;
void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override; void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) override;

View File

@ -2435,6 +2435,9 @@ void VisualScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) c
void VisualScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const { void VisualScriptLanguage::get_public_constants(List<Pair<String, Variant>> *p_constants) const {
} }
void VisualScriptLanguage::get_public_annotations(List<MethodInfo> *p_annotations) const {
}
void VisualScriptLanguage::profiling_start() { void VisualScriptLanguage::profiling_start() {
} }

View File

@ -599,6 +599,7 @@ public:
virtual void get_recognized_extensions(List<String> *p_extensions) const override; virtual void get_recognized_extensions(List<String> *p_extensions) const override;
virtual void get_public_functions(List<MethodInfo> *p_functions) const override; virtual void get_public_functions(List<MethodInfo> *p_functions) const override;
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override; virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override;
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override;
virtual void profiling_start() override; virtual void profiling_start() override;
virtual void profiling_stop() override; virtual void profiling_stop() override;