From 5108913600938257812da6b9b4465b0e08387a8f Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Thu, 19 Sep 2024 17:13:46 +0200 Subject: [PATCH] Add built-in GDScript keywords to the class reference These are part of `@GDScript` and appear in search results as part of a new `builtin` documentable type. (`keyword` is not used, as `keywords` is already the name of an optional attribute for most documentable types.) Keywords can now be Ctrl + clicked in the script editor to go to their class reference description. --- core/doc_data.h | 17 ++ core/object/script_language.h | 2 + core/object/script_language_extension.cpp | 2 + core/object/script_language_extension.h | 10 + doc/class.xsd | 28 +++ doc/classes/ScriptEditor.xml | 2 + doc/classes/ScriptLanguageExtension.xml | 9 +- doc/tools/make_rst.py | 42 ++++ editor/doc_tools.cpp | 45 +++- editor/editor_help.cpp | 130 +++++++++++- editor/editor_help.h | 1 + editor/plugins/script_text_editor.cpp | 3 + modules/gdscript/doc_classes/@GDScript.xml | 199 ++++++++++++++++++ modules/gdscript/gdscript.h | 1 + modules/gdscript/gdscript_editor.cpp | 71 +++++++ .../tests/gdscript_test_runner_suite.h | 14 ++ modules/mono/csharp_script.h | 1 + 17 files changed, 572 insertions(+), 5 deletions(-) diff --git a/core/doc_data.h b/core/doc_data.h index 6a7f4355db54..57d75227a2e6 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -712,6 +712,7 @@ class DocData { Vector constants; HashMap enums; Vector properties; + Vector builtins; Vector annotations; Vector theme_properties; bool is_deprecated = false; @@ -810,6 +811,14 @@ class DocData { doc.properties.push_back(PropertyDoc::from_dict(properties[i])); } + Array builtins; + if (p_dict.has("builtins")) { + builtins = p_dict["builtins"]; + } + for (int i = 0; i < builtins.size(); i++) { + doc.builtins.push_back(MethodDoc::from_dict(builtins[i])); + } + Array annotations; if (p_dict.has("annotations")) { annotations = p_dict["annotations"]; @@ -939,6 +948,14 @@ class DocData { dict["properties"] = properties; } + if (!p_doc.builtins.is_empty()) { + Array builtins; + for (int i = 0; i < p_doc.builtins.size(); i++) { + builtins.push_back(MethodDoc::to_dict(p_doc.builtins[i])); + } + dict["builtins"] = builtins; + } + if (!p_doc.annotations.is_empty()) { Array annotations; for (int i = 0; i < p_doc.annotations.size(); i++) { diff --git a/core/object/script_language.h b/core/object/script_language.h index d0023d70e8ca..a47f05aeb29e 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -346,6 +346,7 @@ class ScriptLanguage : public Object { LOOKUP_RESULT_CLASS_ENUM, LOOKUP_RESULT_CLASS_TBD_GLOBALSCOPE, LOOKUP_RESULT_CLASS_ANNOTATION, + LOOKUP_RESULT_CLASS_BUILTIN, LOOKUP_RESULT_MAX }; @@ -397,6 +398,7 @@ class ScriptLanguage : public Object { /* LOADER FUNCTIONS */ virtual void get_recognized_extensions(List *p_extensions) const = 0; + virtual void get_public_keywords(List *p_keywords) 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; diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index 73f7ec5a5444..e79a44cafb9c 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -146,6 +146,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload"); GDVIRTUAL_BIND(_get_recognized_extensions); + GDVIRTUAL_BIND(_get_public_keywords); GDVIRTUAL_BIND(_get_public_functions); GDVIRTUAL_BIND(_get_public_constants); GDVIRTUAL_BIND(_get_public_annotations); @@ -171,6 +172,7 @@ void ScriptLanguageExtension::_bind_methods() { 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_CLASS_BUILTIN); 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 bc773c5ad343..151eab440b8e 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -600,6 +600,16 @@ class ScriptLanguageExtension : public ScriptLanguage { } } + GDVIRTUAL0RC(TypedArray, _get_public_keywords) + virtual void get_public_keywords(List *p_keywords) const override { + TypedArray ret; + GDVIRTUAL_REQUIRED_CALL(_get_public_keywords, ret); + for (const Variant &var : ret) { + MethodInfo mi = MethodInfo::from_dict(var); + p_keywords->push_back(mi); + } + } + GDVIRTUAL0RC(TypedArray, _get_public_functions) virtual void get_public_functions(List *p_functions) const override { TypedArray ret; diff --git a/doc/class.xsd b/doc/class.xsd index 28e02e870d94..557ea479af4f 100644 --- a/doc/class.xsd +++ b/doc/class.xsd @@ -196,6 +196,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/classes/ScriptEditor.xml b/doc/classes/ScriptEditor.xml index 67a2af29321e..582e9e85d5cb 100644 --- a/doc/classes/ScriptEditor.xml +++ b/doc/classes/ScriptEditor.xml @@ -60,6 +60,8 @@ class_signal:BaseButton:pressed # Shows help for the CanvasItem property visible. class_property:CanvasItem:visible + # Shows help for the GDScript built-in keyword. + class_builtin:@GDScript:for # Shows help for the GDScript annotation export. # Annotations should be prefixed with the `@` symbol in the descriptor, as shown here. class_annotation:@GDScript:@export diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml index f9d9e4f513e1..8f84a04bf4c2 100644 --- a/doc/classes/ScriptLanguageExtension.xml +++ b/doc/classes/ScriptLanguageExtension.xml @@ -189,6 +189,11 @@ + + + + + @@ -391,7 +396,9 @@ - + + + The option is local to the location of the code completion query - e.g. a local variable. Subsequent value of location represent options from the outer class, the exact value represent how far they are (in terms of inner classes). diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 101660881bfa..ed4ae7a5d830 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -45,6 +45,7 @@ "Signals", "Enumerations", "Constants", + "Built-ins", "Annotations", "Property Descriptions", "Constructor Descriptions", @@ -603,6 +604,7 @@ def __init__(self, name: str) -> None: self.methods: OrderedDict[str, List[MethodDef]] = OrderedDict() self.operators: OrderedDict[str, List[MethodDef]] = OrderedDict() self.signals: OrderedDict[str, SignalDef] = OrderedDict() + self.builtins: OrderedDict[str, List[AnnotationDef]] = OrderedDict() self.annotations: OrderedDict[str, List[AnnotationDef]] = OrderedDict() self.theme_items: OrderedDict[str, ThemeItemDef] = OrderedDict() self.inherits: Optional[str] = None @@ -1219,6 +1221,46 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("\n\n") + # Built-in keyword descriptions + if len(class_def.builtins) > 0: + f.write(make_separator(True)) + f.write(make_heading("Built-in keywords", "-")) + + index = 0 + + for method_list in class_def.builtins.values(): # type: ignore + for i, m in enumerate(method_list): + if index != 0: + f.write(make_separator()) + + # Create builtin signature and anchor point. + + self_link = "" + if i == 0: + builtin_anchor = f"class_{class_name}_builtin_{m.name}" + f.write(f".. _{builtin_anchor}:\n\n") + self_link = f" :ref:`🔗<{builtin_anchor}>`" + + f.write(".. rst-class:: classref-builtin\n\n") + + _, signature = make_method_signature(class_def, m, "", state) + f.write(f"{signature}{self_link}\n\n") + + # Add builtin description, or a call to action if it's missing. + + if m.description is not None and m.description.strip() != "": + f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") + else: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this built-in keyword. Please help us by :ref:`contributing one `!" + ) + + "\n\n" + ) + + index += 1 + # Annotation descriptions if len(class_def.annotations) > 0: f.write(make_separator(True)) diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 79e0c7ebd1bc..4a7bc3a7c591 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -341,6 +341,8 @@ void DocTools::merge_from(const DocTools &p_data) { merge_constants(c.constants, cf.constants); + merge_methods(c.builtins, cf.builtins); + merge_methods(c.annotations, cf.annotations); merge_properties(c.properties, cf.properties); @@ -1009,7 +1011,7 @@ void DocTools::generate(BitField p_flags) { // Add scripting language built-ins. { // We only add a doc entry for languages which actually define any built-in - // methods, constants, or annotations. + // keywords (builtins), methods, constants, or annotations. for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); String cname = "@" + lang->get_name(); @@ -1063,6 +1065,39 @@ void DocTools::generate(BitField p_flags) { c.constants.push_back(cd); } + // Get keywords. + List binfo; + lang->get_public_keywords(&binfo); + + for (const MethodInfo &bi : binfo) { + DocData::MethodDoc btd; + btd.name = bi.name; + + if (bi.flags & METHOD_FLAG_VARARG) { + if (!btd.qualifiers.is_empty()) { + btd.qualifiers += " "; + } + btd.qualifiers += "vararg"; + } + + DocData::return_doc_from_retinfo(btd, bi.return_val); + + int j = 0; + for (List::ConstIterator itr = bi.arguments.begin(); itr != bi.arguments.end(); ++itr, ++j) { + DocData::ArgumentDoc ad; + DocData::argument_doc_from_arginfo(ad, *itr); + + int darg_idx = j - (bi.arguments.size() - bi.default_arguments.size()); + if (darg_idx >= 0) { + ad.default_value = DocData::get_default_value_string(bi.default_arguments[darg_idx]); + } + + btd.arguments.push_back(ad); + } + + c.builtins.push_back(btd); + } + // Get annotations. List ainfo; lang->get_public_annotations(&ainfo); @@ -1097,11 +1132,12 @@ void DocTools::generate(BitField p_flags) { } // Skip adding the lang if it doesn't expose anything (e.g. C#). - if (c.methods.is_empty() && c.constants.is_empty() && c.annotations.is_empty()) { + if (c.methods.is_empty() && c.constants.is_empty() && c.builtins.is_empty() && c.annotations.is_empty()) { continue; } c.methods.sort_custom(); + c.builtins.sort_custom(); c.annotations.sort_custom(); class_list[cname] = c; @@ -1350,6 +1386,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 == "builtins") { + Error err2 = _parse_methods(parser, c.builtins); + ERR_FAIL_COND_V(err2, err2); } else if (name2 == "annotations") { Error err2 = _parse_methods(parser, c.annotations); ERR_FAIL_COND_V(err2, err2); @@ -1741,6 +1780,8 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap"); } + _write_method_doc(f, "builtin", c.builtins); + _write_method_doc(f, "annotation", c.annotations); if (!c.theme_properties.is_empty()) { diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index cfe257fcfc8d..58f980b71283 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -276,6 +276,9 @@ void EditorHelp::_class_desc_select(const String &p_select) { } else if (tag == "constant") { topic = "class_constant"; table = &constant_line; + } else if (tag == "builtin") { + topic = "class_builtin"; + table = &builtin_line; } else if (tag == "annotation") { topic = "class_annotation"; table = &annotation_line; @@ -1959,6 +1962,123 @@ void EditorHelp::_update_doc() { } } + // Builtins + if (!cd.builtins.is_empty()) { + if (sort_methods) { + cd.builtins.sort(); + } + + class_desc->add_newline(); + class_desc->add_newline(); + + section_line.push_back(Pair(TTR("Built-in keywords"), class_desc->get_paragraph_count() - 2)); + _push_title_font(); + class_desc->add_text(TTR("Built-in keywords")); + _pop_title_font(); + + for (const DocData::MethodDoc &builtin : cd.builtins) { + class_desc->add_newline(); + class_desc->add_newline(); + + builtin_line[builtin.name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description. + + class_desc->push_indent(1); + + // Builtin header. + _push_code_font(); + _add_bulletpoint(); + + class_desc->push_color(theme_cache.headline_color); + class_desc->add_text(builtin.name); + class_desc->pop(); // color + + if (!builtin.arguments.is_empty()) { + class_desc->push_color(theme_cache.symbol_color); + class_desc->add_text("("); + class_desc->pop(); // color + + for (int j = 0; j < builtin.arguments.size(); j++) { + const DocData::ArgumentDoc &argument = builtin.arguments[j]; + + class_desc->push_color(theme_cache.text_color); + + if (j > 0) { + class_desc->add_text(", "); + } + + class_desc->add_text(argument.name); + class_desc->add_text(": "); + _add_type(argument.type, argument.enumeration, argument.is_bitfield); + + if (!argument.default_value.is_empty()) { + class_desc->push_color(theme_cache.symbol_color); + class_desc->add_text(" = "); + class_desc->pop(); // color + + class_desc->push_color(theme_cache.value_color); + class_desc->add_text(_fix_constant(argument.default_value)); + class_desc->pop(); // color + } + + class_desc->pop(); // color + } + + if (builtin.qualifiers.contains("vararg")) { + class_desc->push_color(theme_cache.text_color); + if (!builtin.arguments.is_empty()) { + class_desc->add_text(", "); + } + class_desc->pop(); // color + + class_desc->push_color(theme_cache.symbol_color); + class_desc->add_text("..."); + class_desc->pop(); // color + } + + class_desc->push_color(theme_cache.symbol_color); + class_desc->add_text(")"); + class_desc->pop(); // color + } + + if (!builtin.qualifiers.is_empty()) { + class_desc->push_color(theme_cache.qualifier_color); + class_desc->add_text(" "); + class_desc->add_text(builtin.qualifiers); + class_desc->pop(); // color + } + + _pop_code_font(); + + class_desc->add_newline(); + + // Builtin description. + class_desc->push_indent(1); + _push_normal_font(); + class_desc->push_color(theme_cache.comment_color); + + const String descr = HANDLE_DOC(builtin.description); + if (!descr.is_empty()) { + _add_text(descr); + } else { + class_desc->add_image(get_editor_theme_icon(SNAME("Error"))); + class_desc->add_text(" "); + class_desc->push_color(theme_cache.comment_color); + if (cd.is_script_doc) { + class_desc->add_text(TTR("There is currently no description for this built-in keyword.")); + } else { + class_desc->append_text(TTR("There is currently no description for this built-in keyword. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text)); + } + class_desc->pop(); // color + } + + class_desc->pop(); // color + _pop_normal_font(); + class_desc->pop(); // indent + + class_desc->pop(); // indent + } + } + // Annotations if (!cd.annotations.is_empty()) { if (sort_methods) { @@ -2353,6 +2473,10 @@ void EditorHelp::_help_callback(const String &p_topic) { if (constant_line.has(name)) { line = constant_line[name]; } + } else if (what == "class_builtin") { + if (builtin_line.has(name)) { + line = builtin_line[name]; + } } else if (what == "class_annotation") { if (annotation_line.has(name)) { line = annotation_line[name]; @@ -2522,7 +2646,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C if (tag != "/img") { p_rt->pop(); } - } else if (tag.begins_with("method ") || tag.begins_with("constructor ") || tag.begins_with("operator ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("annotation ") || tag.begins_with("theme_item ")) { + } else if (tag.begins_with("method ") || tag.begins_with("constructor ") || tag.begins_with("operator ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("builtin ") || tag.begins_with("annotation ") || tag.begins_with("theme_item ")) { const int tag_end = tag.find_char(' '); const String link_tag = tag.left(tag_end); const String link_target = tag.substr(tag_end + 1).lstrip(" "); @@ -2531,7 +2655,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt, const C RichTextLabel::MetaUnderline underline_mode = RichTextLabel::META_UNDERLINE_ON_HOVER; if (link_tag == "method" || link_tag == "constructor" || link_tag == "operator") { target_color = link_method_color; - } else if (link_tag == "member" || link_tag == "signal" || link_tag == "theme_item") { + } else if (link_tag == "member" || link_tag == "signal" || link_tag == "theme_item" || link_tag == "builtin") { target_color = link_property_color; } else if (link_tag == "annotation") { target_color = link_annotation_color; @@ -3652,6 +3776,8 @@ void EditorHelpBit::_meta_clicked(const String &p_select) { topic = "class_signal"; } else if (tag == "constant") { topic = "class_constant"; + } else if (tag == "builtin") { + topic = "class_builtin"; } else if (tag == "annotation") { topic = "class_annotation"; } else if (tag == "theme_item") { diff --git a/editor/editor_help.h b/editor/editor_help.h index 93f74cb2c123..8fd3aedec404 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -107,6 +107,7 @@ class EditorHelp : public VBoxContainer { HashMap property_line; HashMap theme_property_line; HashMap constant_line; + HashMap builtin_line; HashMap annotation_line; HashMap enum_line; HashMap> enum_values_line; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 7e8fba8b9ecc..66bc20a819e7 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1047,6 +1047,9 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c 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_BUILTIN: { + emit_signal(SNAME("go_to_help"), "class_builtin:" + 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 f539f278484f..8e62e76e28b6 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -280,6 +280,205 @@ [b]Warning:[/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. + + + + + AND boolean operator. This evaluates to [code]true[/code] if [b]both[/b] operands evaluate to [code]true[/code]. Otherwise, this evaluates to [code]false[/code]. See also [builtin or]. + [code]&&[/code] can be used as an alternative syntax with the same operator precedence. + [codeblock] + var bucket_full = true + var glass_full = false + + var both_containers_full = bucket_full and glass_full # Evaluates to `false`. + both_containers_full = bucket_full && glass_full # Alternative syntax, also evaluates to `false`. + [/codeblock] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reserved. This keyword causes a script error when used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reserved. This keyword causes a script error when used. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reserved. This keyword causes a script error when used. + + + diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 9bb39aac0f7e..e3d378444620 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -611,6 +611,7 @@ class GDScriptLanguage : public ScriptLanguage { virtual void frame() override; + virtual void get_public_keywords(List *p_keywords) const override; 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; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index cf1cd55355c3..b3610fdc0211 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -434,6 +434,65 @@ void GDScriptLanguage::get_recognized_extensions(List *p_extensions) con p_extensions->push_back("gd"); } +void GDScriptLanguage::get_public_keywords(List *p_keywords) const { + // Keep in sync with `GDScriptLanguage::get_reserved_words()`, but exclude + // function-like and constant-like keywords from this list. + Vector keywords = { + // Control flow. + "break", + "continue", + "elif", + "else", + "for", + "if", + "match", + "pass", + "return", + "when", + "while", + // Declarations. + "class", + "class_name", + "const", + "enum", + "extends", + "func", + "namespace", // Reserved for potential future use. + "signal", + "static", + "trait", // Reserved for potential future use. + "var", + // Other keywords. + "await", + "breakpoint", + "self", + "super", + "yield", // Reserved for potential future use. + // Operators. + "and", + "as", + "in", + "is", + "not", + "or", + // Special values (tokenizer treats them as literals, not as tokens). + "false", + "null", + "true", + // Types (highlighter uses type color instead). + "void", + }; + + for (const String &keyword : keywords) { + MethodInfo mi; + mi.name = keyword; + // PropertyInfo pi; + // pi.type = Variant::NIL; + // mi.return_val = pi; + p_keywords->push_back(mi); + } +} + void GDScriptLanguage::get_public_functions(List *p_functions) const { List functions; GDScriptUtilityFunctions::get_function_list(&functions); @@ -3841,6 +3900,18 @@ ::Error GDScriptLanguage::lookup_code(const String &p_code, const String &p_symb r_result.class_member = p_symbol; return OK; } + + // Lookup keywords after constants, as some reserved words are constants (like `PI`). + List keywords; + get_reserved_words(&keywords); + for (const String &keyword : keywords) { + if (keyword == p_symbol) { + r_result.type = ScriptLanguage::LOOKUP_RESULT_CLASS_BUILTIN; + r_result.class_name = "@GDScript"; + r_result.class_member = p_symbol; + return OK; + } + } } GDScriptAnalyzer analyzer(&parser); diff --git a/modules/gdscript/tests/gdscript_test_runner_suite.h b/modules/gdscript/tests/gdscript_test_runner_suite.h index d6befd2db3f5..2f18ed8ee6f5 100644 --- a/modules/gdscript/tests/gdscript_test_runner_suite.h +++ b/modules/gdscript/tests/gdscript_test_runner_suite.h @@ -89,6 +89,20 @@ TEST_CASE("[Modules][GDScript] Validate built-in API") { } } + // Validate builtins. + List builtins; + lang->get_public_keywords(&builtins); + + SUBCASE("[Modules][GDScript] Validate built-in keywords") { + for (const MethodInfo &bi : builtins) { + int i = 0; + for (List::ConstIterator itr = bi.arguments.begin(); itr != bi.arguments.end(); ++itr, ++i) { + TEST_COND((itr->name.is_empty() || itr->name.begins_with("_unnamed_arg")), + vformat("Unnamed argument in position %d of built-in keyword '%s'.", i, bi.name)); + } + } + } + // Validate annotations. List builtin_annotations; lang->get_public_annotations(&builtin_annotations); diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index ec7328be4a4f..cf035ffc9655 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -553,6 +553,7 @@ class CSharpLanguage : public ScriptLanguage { void frame() override; + /* TODO? */ void get_public_keywords(List *p_keywords) const override {} /* 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 {}