From a9098e6147d294378bf7c62fb10c83a0d2670b33 Mon Sep 17 00:00:00 2001 From: Yuri Sizov Date: Mon, 4 Jul 2022 18:56:34 +0300 Subject: [PATCH] Add support for documenting built-in annotations --- core/doc_data.h | 1 + core/object/script_language.h | 2 + core/object/script_language_extension.cpp | 2 + core/object/script_language_extension.h | 9 ++ doc/class.xsd | 36 ++++++ doc/classes/ScriptLanguageExtension.xml | 9 +- doc/tools/make_rst.py | 48 +++++++ editor/doc_tools.cpp | 54 +++++++- editor/editor_help.cpp | 143 ++++++++++++++++++--- editor/editor_help.h | 1 + editor/plugins/script_text_editor.cpp | 3 + modules/gdscript/doc_classes/@GDScript.xml | 141 ++++++++++++++++++++ modules/gdscript/gdscript.h | 1 + modules/gdscript/gdscript_editor.cpp | 19 +++ modules/gdscript/gdscript_parser.cpp | 4 + modules/gdscript/gdscript_parser.h | 1 + modules/mono/csharp_script.h | 1 + modules/visual_script/visual_script.cpp | 3 + modules/visual_script/visual_script.h | 1 + 19 files changed, 462 insertions(+), 17 deletions(-) diff --git a/core/doc_data.h b/core/doc_data.h index af20b717d78..75c155c4bf6 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -163,6 +163,7 @@ public: Vector constants; HashMap enums; Vector properties; + Vector annotations; Vector theme_properties; bool is_script_doc = false; String script_path; diff --git a/core/object/script_language.h b/core/object/script_language.h index 776a9bfaab5..686ab5b8d33 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -350,6 +350,7 @@ public: LOOKUP_RESULT_CLASS_SIGNAL, LOOKUP_RESULT_CLASS_ENUM, LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE, + LOOKUP_RESULT_CLASS_ANNOTATION, LOOKUP_RESULT_MAX }; @@ -402,6 +403,7 @@ public: virtual void get_recognized_extensions(List *p_extensions) const = 0; virtual void get_public_functions(List *p_functions) const = 0; virtual void get_public_constants(List> *p_constants) const = 0; + virtual void get_public_annotations(List *p_annotations) const = 0; struct ProfilingInfo { StringName signature; diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index 5af79bbea35..ab8dd6d1ee6 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -134,6 +134,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_get_recognized_extensions); GDVIRTUAL_BIND(_get_public_functions); GDVIRTUAL_BIND(_get_public_constants); + GDVIRTUAL_BIND(_get_public_annotations); GDVIRTUAL_BIND(_profiling_start); 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_ENUM); BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE); + BIND_ENUM_CONSTANT(LOOKUP_RESULT_CLASS_ANNOTATION); BIND_ENUM_CONSTANT(LOOKUP_RESULT_MAX); BIND_ENUM_CONSTANT(LOCATION_LOCAL); diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 7eea48370e1..2c53139ec2b 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -580,6 +580,15 @@ public: p_constants->push_back(Pair(d["name"], d["value"])); } } + GDVIRTUAL0RC(TypedArray, _get_public_annotations) + virtual void get_public_annotations(List *p_annotations) const override { + TypedArray 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_stop) diff --git a/doc/class.xsd b/doc/class.xsd index 498c930d6fe..e8aebd609d9 100644 --- a/doc/class.xsd +++ b/doc/class.xsd @@ -162,6 +162,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml index 0f757cf8062..45d4cf44fa1 100644 --- a/doc/classes/ScriptLanguageExtension.xml +++ b/doc/classes/ScriptLanguageExtension.xml @@ -174,6 +174,11 @@ + + + + + @@ -378,7 +383,9 @@ - + + + The option is local to the location of the code completion query - e.g. a local variable. diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index ce09361dfae..c357f93b60a 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -36,6 +36,7 @@ BASE_STRINGS = [ "Signals", "Enumerations", "Constants", + "Annotations", "Property Descriptions", "Constructor Descriptions", "Method Descriptions", @@ -155,6 +156,7 @@ class ClassDef: self.methods = OrderedDict() # type: OrderedDict[str, List[MethodDef]] self.operators = OrderedDict() # type: OrderedDict[str, List[MethodDef]] self.signals = OrderedDict() # type: OrderedDict[str, SignalDef] + self.annotations = OrderedDict() # type: OrderedDict[str, List[MethodDef]] self.theme_items = OrderedDict() # type: OrderedDict[str, ThemeItemDef] self.inherits = None # type: Optional[str] self.brief_description = None # type: Optional[str] @@ -323,6 +325,27 @@ class State: 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") if signals is not None: 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") + 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 if any(not p.overrides for p in class_def.properties.values()) > 0: 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) 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"): found = False diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index e6cd1e0b482..f7ae40c2e67 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -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++) { DocData::PropertyDoc &p = c.properties.write[i]; @@ -959,8 +973,41 @@ void DocTools::generate(bool p_basic_types) { c.constants.push_back(cd); } + // Get annotations. + List 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#). - if (c.methods.is_empty() && c.constants.is_empty()) { + if (c.methods.is_empty() && c.constants.is_empty() && c.annotations.is_empty()) { continue; } @@ -1162,6 +1209,9 @@ Error DocTools::_load(Ref parser) { } else if (name2 == "signals") { Error err2 = _parse_methods(parser, c.signals); 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") { while (parser->read() == OK) { if (parser->get_node_type() == XMLParser::NODE_ELEMENT) { @@ -1442,6 +1492,8 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap"); } + _write_method_doc(f, "annotation", c.annotations); + if (!c.theme_properties.is_empty()) { c.theme_properties.sort(); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 36360954d99..2c43a4fbc97 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -125,6 +125,9 @@ void EditorHelp::_class_desc_select(const String &p_select) { } else if (tag == "constant") { topic = "class_constant"; table = &this->constant_line; + } else if (tag == "annotation") { + topic = "class_annotation"; + table = &this->annotation_line; } else if (tag == "theme_item") { topic = "theme_item"; 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(" "); } - 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); } @@ -282,7 +285,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview _add_text(p_method.name); 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 } @@ -412,7 +415,7 @@ void EditorHelp::_update_method_list(const Vector p_methods, 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; } @@ -611,7 +614,7 @@ void EditorHelp::_update_doc() { class_desc->add_newline(); // 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_font(doc_bold_font); class_desc->push_indent(1); @@ -625,7 +628,7 @@ void EditorHelp::_update_doc() { } // Class description - if (!cd.description.is_empty()) { + if (!cd.description.strip_edges().is_empty()) { section_line.push_back(Pair(TTR("Description"), class_desc->get_paragraph_count() - 2)); description_line = class_desc->get_paragraph_count() - 2; class_desc->push_color(title_color); @@ -692,7 +695,7 @@ void EditorHelp::_update_doc() { if (cd.is_script_doc) { has_properties = false; 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; } has_properties = true; @@ -718,7 +721,7 @@ void EditorHelp::_update_doc() { for (int i = 0; i < cd.properties.size(); i++) { // 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; } 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; } - if (!cd.properties[i].description.is_empty()) { + if (!cd.properties[i].description.strip_edges().is_empty()) { describe = true; } @@ -856,7 +859,7 @@ void EditorHelp::_update_doc() { } } // 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; } methods.push_back(cd.methods[i]); @@ -976,7 +979,7 @@ void EditorHelp::_update_doc() { class_desc->pop(); // monofont // 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_color(comment_color); 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. class_desc->push_font(doc_code_font); // monofont - class_desc->push_color(headline_color); _add_bulletpoint(); + class_desc->push_color(headline_color); _add_text(cd.signals[i].name); class_desc->pop(); class_desc->push_color(symbol_color); @@ -1048,7 +1051,7 @@ void EditorHelp::_update_doc() { class_desc->add_text(")"); class_desc->pop(); 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_color(comment_color); class_desc->push_indent(1); @@ -1079,7 +1082,7 @@ void EditorHelp::_update_doc() { enums[cd.constants[i].enumeration].push_back(cd.constants[i]); } else { // 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; } 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; class_desc->push_font(doc_code_font); - class_desc->push_color(headline_color); _add_bulletpoint(); + class_desc->push_color(headline_color); _add_text(enum_list[i].name); class_desc->pop(); class_desc->push_color(symbol_color); @@ -1236,7 +1239,7 @@ void EditorHelp::_update_doc() { 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_color(comment_color); _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(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 if (property_descr) { section_line.push_back(Pair(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)) { line = constant_line[name]; } + } else if (what == "class_annotation") { + if (annotation_line.has(name)) { + line = annotation_line[name]; + } } else if (what == "class_global") { if (constant_line.has(name)) { line = constant_line[name]; diff --git a/editor/editor_help.h b/editor/editor_help.h index 7f91a8102df..9f8da938f1a 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -110,6 +110,7 @@ class EditorHelp : public VBoxContainer { HashMap property_line; HashMap theme_property_line; HashMap constant_line; + HashMap annotation_line; HashMap enum_line; HashMap> enum_values_line; int description_line = 0; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 7d4ffd1a252..7c3520c39d9 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -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); } 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: { emit_signal(SNAME("go_to_help"), "class_global:" + result.class_name + ":" + result.class_member); } break; diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 70151c4d215..e672ea64542 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index feb0a237df4..e9a206f48b5 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -489,6 +489,7 @@ public: virtual void get_public_functions(List *p_functions) const override; virtual void get_public_constants(List> *p_constants) const override; + virtual void get_public_annotations(List *p_annotations) const override; virtual void profiling_start() override; virtual void profiling_stop() override; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index 474c8094f2e..d62104fb5bb 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -445,6 +445,16 @@ void GDScriptLanguage::get_public_constants(List> *p_const p_constants->push_back(nan); } +void GDScriptLanguage::get_public_annotations(List *p_annotations) const { + GDScriptParser parser; + List 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 { #ifdef TOOLS_ENABLED 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; } } 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: { } } diff --git a/modules/gdscript/gdscript_parser.cpp b/modules/gdscript/gdscript_parser.cpp index ca430b0f727..b69eef0a756 100644 --- a/modules/gdscript/gdscript_parser.cpp +++ b/modules/gdscript/gdscript_parser.cpp @@ -105,6 +105,10 @@ void GDScriptParser::get_annotation_list(List *r_annotations) const } } +bool GDScriptParser::annotation_exists(const String &p_annotation_name) const { + return valid_annotations.has(p_annotation_name); +} + GDScriptParser::GDScriptParser() { // Register valid annotations. // TODO: Should this be static? diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h index e3f8d4b8baa..8f3862b3247 100644 --- a/modules/gdscript/gdscript_parser.h +++ b/modules/gdscript/gdscript_parser.h @@ -1413,6 +1413,7 @@ public: CompletionContext get_completion_context() const { return completion_context; } CompletionCall get_completion_call() const { return completion_call; } void get_annotation_list(List *r_annotations) const; + bool annotation_exists(const String &p_annotation_name) const; const List &get_errors() const { return errors; } const List get_dependencies() const { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 69bd8703aab..91b14ba1085 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -502,6 +502,7 @@ public: /* TODO? */ void get_public_functions(List *p_functions) const override {} /* TODO? */ void get_public_constants(List> *p_constants) const override {} + /* TODO? */ void get_public_annotations(List *p_annotations) const override {} void reload_all_scripts() override; void reload_tool_script(const Ref