From 1ad16b3d4a2b371208f66085ae3106b59981dbf9 Mon Sep 17 00:00:00 2001 From: Ignacio Etcheverry Date: Mon, 18 Mar 2019 21:59:35 +0100 Subject: [PATCH] C#: Bindings generator now translates BBCode docs to XML comments --- modules/mono/editor/bindings_generator.cpp | 529 ++++++++++++++++++--- modules/mono/editor/bindings_generator.h | 22 +- 2 files changed, 472 insertions(+), 79 deletions(-) diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 890bea0d1d5..a6b5c1535b4 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -38,6 +38,7 @@ #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/os.h" +#include "core/string_builder.h" #include "core/ucaps.h" #include "../glue/cs_compressed.gen.h" @@ -105,6 +106,16 @@ bool BindingsGenerator::verbose_output = false; BindingsGenerator *BindingsGenerator::singleton = NULL; +static String fix_doc_description(const String &p_bbcode) { + + // This seems to be the correct way to do this. It's the same EditorHelp does. + + return p_bbcode.dedent() + .replace("\t", "") + .replace("\r", "") + .strip_edges(); +} + static String snake_to_pascal_case(const String &p_identifier, bool p_input_is_upper = false) { String ret; @@ -173,6 +184,366 @@ static String snake_to_camel_case(const String &p_identifier, bool p_input_is_up return ret; } +String BindingsGenerator::bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype) { + + // Based on the version in EditorHelp + + if (p_bbcode.empty()) + return String(); + + DocData *doc = EditorHelp::get_doc_data(); + + String bbcode = p_bbcode; + + StringBuilder xml_output; + + xml_output.append(""); + + List tag_stack; + bool code_tag = false; + + int pos = 0; + while (pos < bbcode.length()) { + int brk_pos = bbcode.find("[", pos); + + if (brk_pos < 0) + brk_pos = bbcode.length(); + + if (brk_pos > pos) { + String text = bbcode.substr(pos, brk_pos - pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) + xml_output.append(""); + + xml_output.append(lines[i].xml_escape()); + + if (i != lines.size() - 1) + xml_output.append("\n"); + } + } + } + + if (brk_pos == bbcode.length()) + break; // nothing else to add + + int brk_end = bbcode.find("]", brk_pos + 1); + + if (brk_end == -1) { + String text = bbcode.substr(brk_pos, bbcode.length() - brk_pos); + if (code_tag || tag_stack.size() > 0) { + xml_output.append(text.xml_escape()); + } else { + Vector lines = text.split("\n"); + for (int i = 0; i < lines.size(); i++) { + if (i != 0) + xml_output.append(""); + + xml_output.append(lines[i].xml_escape()); + + if (i != lines.size() - 1) + xml_output.append("\n"); + } + } + + break; + } + + String tag = bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1); + + if (tag.begins_with("/")) { + bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); + + if (!tag_ok) { + xml_output.append("["); + pos = brk_pos + 1; + continue; + } + + tag_stack.pop_front(); + pos = brk_end + 1; + code_tag = false; + + if (tag == "/url") { + xml_output.append(""); + } else if (tag == "/code") { + xml_output.append(""); + } else if (tag == "/codeblock") { + xml_output.append(""); + } + } else if (code_tag) { + xml_output.append("["); + pos = brk_pos + 1; + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ")) { + String link_target = tag.substr(tag.find(" ") + 1, tag.length()); + String link_tag = tag.substr(0, tag.find(" ")); + + Vector link_target_parts = link_target.split("."); + + if (link_target_parts.size() <= 0 || link_target_parts.size() > 2) { + ERR_PRINTS("Invalid reference format: " + tag); + + xml_output.append(""); + xml_output.append(tag); + xml_output.append(""); + + pos = brk_end + 1; + continue; + } + + const TypeInterface *target_itype; + StringName target_cname; + + if (link_target_parts.size() == 2) { + target_itype = _get_type_or_null(TypeReference(link_target_parts[0])); + if (!target_itype) { + target_itype = _get_type_or_null(TypeReference("_" + link_target_parts[0])); + } + target_cname = link_target_parts[1]; + } else { + target_itype = p_itype; + target_cname = link_target_parts[0]; + } + + if (link_tag == "method") { + if (!target_itype || !target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (target_itype) { + OS::get_singleton()->print("Cannot resolve method reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from method reference in documentation: %s\n", link_target.utf8().get_data()); + } + } + + // TODO Map what we can + xml_output.append(""); + xml_output.append(link_target); + xml_output.append(""); + } else { + const MethodInterface *target_imethod = target_itype->find_method_by_name(target_cname); + + if (target_imethod) { + xml_output.append("proxy_name); + xml_output.append("."); + xml_output.append(target_imethod->proxy_name); + xml_output.append("\"/>"); + } + } + } else if (link_tag == "member") { + if (!target_itype || !target_itype->is_object_type) { + if (OS::get_singleton()->is_stdout_verbose()) { + if (target_itype) { + OS::get_singleton()->print("Cannot resolve member reference for non-Godot.Object type in documentation: %s\n", link_target.utf8().get_data()); + } else { + OS::get_singleton()->print("Cannot resolve type from member reference in documentation: %s\n", link_target.utf8().get_data()); + } + } + + // TODO Map what we can + xml_output.append(""); + xml_output.append(link_target); + xml_output.append(""); + } else { + const PropertyInterface *target_iprop = target_itype->find_property_by_name(target_cname); + + if (target_iprop) { + xml_output.append("proxy_name); + xml_output.append("."); + xml_output.append(target_iprop->proxy_name); + xml_output.append("\"/>"); + } + } + } else if (link_tag == "signal") { + // We do not declare signals in any way in C#, so there is nothing to reference + xml_output.append(""); + xml_output.append(link_target); + xml_output.append(""); + } else if (link_tag == "enum") { + StringName search_cname = !target_itype ? target_cname : + StringName(target_itype->name + "." + (String)target_cname); + + const Map::Element *enum_match = enum_types.find(search_cname); + + if (!enum_match && search_cname != target_cname) { + enum_match = enum_types.find(target_cname); + } + + if (enum_match) { + const TypeInterface &target_enum_itype = enum_match->value(); + + xml_output.append(""); + } else { + ERR_PRINTS("Cannot resolve enum reference in documentation: " + link_target); + + xml_output.append(""); + xml_output.append(link_target); + xml_output.append(""); + } + } + + pos = brk_end + 1; + } else if (doc->class_list.has(tag)) { + if (tag == "Array" || tag == "Dictionary") { + xml_output.append(""); + } else if (tag == "bool" || tag == "int") { + xml_output.append(""); + } else if (tag == "float") { + xml_output.append(""); + } else if (tag == "Variant") { + // We use System.Object for Variant, so there is no Variant type in C# + xml_output.append("Variant"); + } else if (tag == "String") { + xml_output.append(""); + } else if (tag == "Nil") { + xml_output.append(""); + } else if (tag.begins_with("@")) { + // @Global Scope, @GDScript, etc + xml_output.append(""); + xml_output.append(tag); + xml_output.append(""); + } else if (tag == "PoolByteArray") { + xml_output.append(""); + } else if (tag == "PoolIntArray") { + xml_output.append(""); + } else if (tag == "PoolRealArray") { +#ifdef REAL_T_IS_DOUBLE + xml_output.append(""); +#else + xml_output.append(""); +#endif + } else if (tag == "PoolStringArray") { + xml_output.append(""); + } else if (tag == "PoolVector2Array") { + xml_output.append(""); + } else if (tag == "PoolVector3Array") { + xml_output.append(""); + } else if (tag == "PoolColorArray") { + xml_output.append(""); + } else { + const TypeInterface *target_itype = _get_type_or_null(TypeReference(tag)); + + if (!target_itype) { + target_itype = _get_type_or_null(TypeReference("_" + tag)); + } + + if (target_itype) { + xml_output.append("proxy_name); + xml_output.append("\"/>"); + } else { + ERR_PRINTS("Cannot resolve type reference in documentation: " + tag); + + xml_output.append(""); + xml_output.append(tag); + xml_output.append(""); + } + } + + pos = brk_end + 1; + } else if (tag == "b") { + // bold is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "i") { + // italics is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "code") { + xml_output.append(""); + + code_tag = true; + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "codeblock") { + xml_output.append(""); + + code_tag = true; + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "center") { + // center is alignment not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "br") { + xml_output.append("\n"); // FIXME: Should use instead. Luckily this tag isn't used for now. + pos = brk_end + 1; + } else if (tag == "u") { + // underline is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "s") { + // strikethrough is not supported in xml comments + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag == "url") { + int end = bbcode.find("[", brk_end); + if (end == -1) + end = bbcode.length(); + String url = bbcode.substr(brk_end + 1, end - brk_end - 1); + xml_output.append(""); + xml_output.append(url); + + pos = brk_end + 1; + tag_stack.push_front(tag); + } else if (tag.begins_with("url=")) { + String url = tag.substr(4, tag.length()); + xml_output.append(""); + + pos = brk_end + 1; + tag_stack.push_front("url"); + } else if (tag == "img") { + int end = bbcode.find("[", brk_end); + if (end == -1) + end = bbcode.length(); + String image = bbcode.substr(brk_end + 1, end - brk_end - 1); + + // Not supported. Just append the bbcode. + xml_output.append("[img]"); + xml_output.append(image); + xml_output.append("[/img]"); + + pos = end; + tag_stack.push_front(tag); + } else if (tag.begins_with("color=")) { + // Not supported. + pos = brk_end + 1; + tag_stack.push_front("color"); + } else if (tag.begins_with("font=")) { + // Not supported. + pos = brk_end + 1; + tag_stack.push_front("font"); + } else { + xml_output.append("["); // ignore + pos = brk_pos + 1; + } + } + + xml_output.append(""); + + return xml_output.as_string(); +} + int BindingsGenerator::_determine_enum_prefix(const EnumInterface &p_ienum) { CRASH_COND(p_ienum.constants.empty()); @@ -299,6 +670,9 @@ void BindingsGenerator::_generate_global_constants(List &p_output) { // Constants (in partial GD class) + p_output.push_back("\n#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n"); + p_output.push_back("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); p_output.push_back(INDENT1 "public static partial class " BINDINGS_GLOBAL_SCOPE_CLASS "\n" INDENT1 "{"); @@ -306,20 +680,20 @@ void BindingsGenerator::_generate_global_constants(List &p_output) { const ConstantInterface &iconstant = E->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - p_output.push_back(MEMBER_BEGIN "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + p_output.push_back(MEMBER_BEGIN "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); + p_output.push_back(summary_lines[i]); p_output.push_back("\n"); } - } - p_output.push_back(INDENT2 "/// "); + p_output.push_back(INDENT2 "/// "); + } } p_output.push_back(MEMBER_BEGIN "public const int "); @@ -369,20 +743,20 @@ void BindingsGenerator::_generate_global_constants(List &p_output) { const ConstantInterface &iconstant = F->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - p_output.push_back(INDENT2 "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + p_output.push_back(INDENT2 "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); + p_output.push_back(summary_lines[i]); p_output.push_back("\n"); } - } - p_output.push_back(INDENT2 "/// \n"); + p_output.push_back(INDENT2 "/// \n"); + } } p_output.push_back(INDENT2); @@ -399,6 +773,8 @@ void BindingsGenerator::_generate_global_constants(List &p_output) { } p_output.push_back(CLOSE_BLOCK); // end of namespace + + p_output.push_back("\n#pragma warning restore CS1591\n"); } Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution, bool p_verbose_output) { @@ -712,28 +1088,31 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back("using System;\n"); // IntPtr output.push_back("using System.Diagnostics;\n"); // DebuggerBrowsable - output.push_back("\n#pragma warning disable CS1591 // Disable warning: " - "'Missing XML comment for publicly visible type or member'\n"); + output.push_back("\n" + "#pragma warning disable CS1591 // Disable warning: " + "'Missing XML comment for publicly visible type or member'\n" + "#pragma warning disable CS1573 // Disable warning: " + "'Parameter has no matching param tag in the XML comment'\n"); output.push_back("\nnamespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK); const DocData::ClassDoc *class_doc = itype.class_doc; if (class_doc && class_doc->description.size()) { - output.push_back(INDENT1 "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = class_doc->description.split("\n"); + if (summary_lines.size()) { + output.push_back(INDENT1 "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { output.push_back(INDENT1 "/// "); - output.push_back(description_line.xml_escape()); + output.push_back(summary_lines[i]); output.push_back("\n"); } - } - output.push_back(INDENT1 "/// \n"); + output.push_back(INDENT1 "/// \n"); + } } output.push_back(INDENT1 "public "); @@ -767,20 +1146,20 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str const ConstantInterface &iconstant = E->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - output.push_back(MEMBER_BEGIN "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + output.push_back(MEMBER_BEGIN "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { output.push_back(INDENT2 "/// "); - output.push_back(description_line.xml_escape()); + output.push_back(summary_lines[i]); output.push_back("\n"); } - } - output.push_back(INDENT2 "/// "); + output.push_back(INDENT2 "/// "); + } } output.push_back(MEMBER_BEGIN "public const int "); @@ -808,20 +1187,20 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str const ConstantInterface &iconstant = F->get(); if (iconstant.const_doc && iconstant.const_doc->description.size()) { - output.push_back(INDENT3 "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = iconstant.const_doc->description.split("\n"); + if (summary_lines.size()) { + output.push_back(INDENT3 "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { output.push_back(INDENT3 "/// "); - output.push_back(description_line.xml_escape()); + output.push_back(summary_lines[i]); output.push_back("\n"); } - } - output.push_back(INDENT3 "/// \n"); + output.push_back(INDENT3 "/// \n"); + } } output.push_back(INDENT3); @@ -928,7 +1307,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str output.push_back(INDENT1 CLOSE_BLOCK /* class */ CLOSE_BLOCK /* namespace */); - output.push_back("\n#pragma warning restore CS1591\n"); + output.push_back("\n" + "#pragma warning restore CS1591\n" + "#pragma warning restore CS1573\n"); return _save_file(p_output_file, output); } @@ -978,33 +1359,21 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte const TypeInterface *prop_itype = _get_type_or_null(proptype_name); ERR_FAIL_NULL_V(prop_itype, ERR_BUG); // Property type not found - String prop_proxy_name = escape_csharp_keyword(snake_to_pascal_case(p_iprop.cname)); - - // Prevent property and enclosing type from sharing the same name - if (prop_proxy_name == p_itype.proxy_name) { - if (verbose_output) { - WARN_PRINTS("Name of property `" + prop_proxy_name + "` is ambiguous with the name of its class `" + - p_itype.proxy_name + "`. Renaming property to `" + prop_proxy_name + "_`"); - } - - prop_proxy_name += "_"; - } - if (p_iprop.prop_doc && p_iprop.prop_doc->description.size()) { - p_output.push_back(MEMBER_BEGIN "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = p_iprop.prop_doc->description.split("\n"); + if (summary_lines.size()) { + p_output.push_back(MEMBER_BEGIN "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); + p_output.push_back(summary_lines[i]); p_output.push_back("\n"); } - } - p_output.push_back(INDENT2 "/// "); + p_output.push_back(INDENT2 "/// "); + } } p_output.push_back(MEMBER_BEGIN "public "); @@ -1014,7 +1383,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte p_output.push_back(prop_itype->cs_type); p_output.push_back(" "); - p_output.push_back(prop_proxy_name.replace("/", "__")); + p_output.push_back(p_iprop.proxy_name); p_output.push_back("\n" INDENT2 OPEN_BLOCK); if (getter) { @@ -1135,7 +1504,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf icall_params += arg_type->cs_in.empty() ? arg_in : sformat(arg_type->cs_in, arg_in); - default_args_doc.push_back(INDENT2 "/// If the param is null, then the default value is " + def_arg + "\n"); + // Apparently the name attribute must not include the @ + String param_tag_name = iarg.name.begins_with("@") ? iarg.name.substr(1, iarg.name.length()) : iarg.name; + + default_args_doc.push_back(INDENT2 "/// If the parameter is null, then the default value is " + def_arg + "\n"); } else { icall_params += arg_type->cs_in.empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name); } @@ -1151,24 +1523,24 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf } if (p_imethod.method_doc && p_imethod.method_doc->description.size()) { - p_output.push_back(MEMBER_BEGIN "/// \n"); + String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype); + Vector summary_lines = xml_summary.split("\n"); - Vector description_lines = p_imethod.method_doc->description.split("\n"); + if (summary_lines.size() || default_args_doc.size()) { + p_output.push_back(MEMBER_BEGIN "/// \n"); - for (int i = 0; i < description_lines.size(); i++) { - String description_line = description_lines[i].strip_edges(); - if (description_line.size()) { + for (int i = 0; i < summary_lines.size(); i++) { p_output.push_back(INDENT2 "/// "); - p_output.push_back(description_line.xml_escape()); + p_output.push_back(summary_lines[i]); p_output.push_back("\n"); } - } - for (List::Element *E = default_args_doc.front(); E; E = E->next()) { - p_output.push_back(E->get().xml_escape()); - } + for (List::Element *E = default_args_doc.front(); E; E = E->next()) { + p_output.push_back(E->get()); + } - p_output.push_back(INDENT2 "/// "); + p_output.push_back(INDENT2 "/// "); + } } if (!p_imethod.is_internal) { @@ -1728,7 +2100,6 @@ void BindingsGenerator::_populate_object_type_interfaces() { PropertyInterface iprop; iprop.cname = property.name; - iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); iprop.setter = ClassDB::get_property_setter(type_cname, iprop.cname); iprop.getter = ClassDB::get_property_getter(type_cname, iprop.cname); @@ -1736,6 +2107,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { iprop.index = ClassDB::get_property_index(type_cname, iprop.cname, &valid); ERR_FAIL_COND(!valid); + iprop.proxy_name = escape_csharp_keyword(snake_to_pascal_case(iprop.cname)); + // Prevent property and enclosing type from sharing the same name if (iprop.proxy_name == itype.proxy_name) { if (verbose_output) { @@ -1746,6 +2119,8 @@ void BindingsGenerator::_populate_object_type_interfaces() { iprop.proxy_name += "_"; } + iprop.proxy_name = iprop.proxy_name.replace("/", "__"); // Some members have a slash... + iprop.prop_doc = NULL; for (int i = 0; i < itype.class_doc->properties.size(); i++) { diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 8a1c942f6bd..7eaebeabbd6 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -87,8 +87,13 @@ class BindingsGenerator { StringName cname; bool is_enum; - TypeReference() { - is_enum = false; + TypeReference() : + is_enum(false) { + } + + TypeReference(const StringName &p_cname) : + cname(p_cname), + is_enum(false) { } }; @@ -321,6 +326,15 @@ class BindingsGenerator { return NULL; } + const PropertyInterface *find_property_by_name(const StringName &p_cname) const { + for (const List::Element *E = properties.front(); E; E = E->next()) { + if (E->get().cname == p_cname) + return &E->get(); + } + + return NULL; + } + const PropertyInterface *find_property_by_proxy_name(const String &p_proxy_name) const { for (const List::Element *E = properties.front(); E; E = E->next()) { if (E->get().proxy_name == p_proxy_name) @@ -482,6 +496,7 @@ class BindingsGenerator { StringName type_VarArg; StringName type_Object; StringName type_Reference; + StringName type_String; StringName enum_Error; NameCache() { @@ -493,6 +508,7 @@ class BindingsGenerator { type_VarArg = StaticCString::create("VarArg"); type_Object = StaticCString::create("Object"); type_Reference = StaticCString::create("Reference"); + type_String = StaticCString::create("String"); enum_Error = StaticCString::create("Error"); } @@ -522,6 +538,8 @@ class BindingsGenerator { return p_type.name; } + String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype); + int _determine_enum_prefix(const EnumInterface &p_ienum); void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);