summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/gdnative/nativescript/nativescript.cpp40
-rw-r--r--modules/gdnative/nativescript/nativescript.h1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp190
-rw-r--r--modules/gdscript/gdscript_compiler.h9
-rw-r--r--modules/mono/editor/bindings_generator.cpp529
-rw-r--r--modules/mono/editor/bindings_generator.h22
-rw-r--r--modules/mono/editor/script_class_parser.cpp14
-rw-r--r--modules/mono/utils/mono_reg_utils.cpp8
8 files changed, 583 insertions, 230 deletions
diff --git a/modules/gdnative/nativescript/nativescript.cpp b/modules/gdnative/nativescript/nativescript.cpp
index 2da9d6bfdc..5cf144d4fe 100644
--- a/modules/gdnative/nativescript/nativescript.cpp
+++ b/modules/gdnative/nativescript/nativescript.cpp
@@ -160,8 +160,10 @@ bool NativeScript::can_instance() const {
NativeScriptDesc *script_data = get_script_desc();
#ifdef TOOLS_ENABLED
-
- return script_data || (!is_tool() && !ScriptServer::is_scripting_enabled());
+ // Only valid if this is either a tool script or a "regular" script.
+ // (so an environment whre scripting is disabled (and not the editor) would not
+ // create objects).
+ return script_data && (is_tool() || ScriptServer::is_scripting_enabled());
#else
return script_data;
#endif
@@ -199,25 +201,6 @@ ScriptInstance *NativeScript::instance_create(Object *p_this) {
return NULL;
}
-#ifdef TOOLS_ENABLED
- if (!ScriptServer::is_scripting_enabled() && !is_tool()) {
- // placeholder for nodes. For tools we want the rool thing.
-
- PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(NSL, Ref<Script>(this), p_this));
- placeholders.insert(sins);
-
- if (script_data->create_func.create_func) {
- script_data->create_func.create_func(
- (godot_object *)p_this,
- script_data->create_func.method_data);
- }
-
- _update_placeholder(sins);
-
- return sins;
- }
-#endif
-
NativeScriptInstance *nsi = memnew(NativeScriptInstance);
nsi->owner = p_this;
@@ -246,6 +229,19 @@ ScriptInstance *NativeScript::instance_create(Object *p_this) {
return nsi;
}
+PlaceHolderScriptInstance *NativeScript::placeholder_instance_create(Object *p_this) {
+#ifdef TOOLS_ENABLED
+ PlaceHolderScriptInstance *sins = memnew(PlaceHolderScriptInstance(NSL, Ref<Script>(this), p_this));
+ placeholders.insert(sins);
+
+ _update_placeholder(sins);
+
+ return sins;
+#else
+ return NULL;
+#endif
+}
+
bool NativeScript::instance_has(const Object *p_this) const {
return instance_owners.has((Object *)p_this);
}
@@ -1691,7 +1687,7 @@ void NativeReloadNode::_notification(int p_what) {
// since singleton libraries are not unloaded there is no point
// in loading them again.
- if (!gdn->get_library()->is_singleton()) {
+ if (gdn->get_library()->is_singleton()) {
continue;
}
diff --git a/modules/gdnative/nativescript/nativescript.h b/modules/gdnative/nativescript/nativescript.h
index 8dd5ba3b9c..a6865c6243 100644
--- a/modules/gdnative/nativescript/nativescript.h
+++ b/modules/gdnative/nativescript/nativescript.h
@@ -149,6 +149,7 @@ public:
virtual StringName get_instance_base_type() const; // this may not work in all scripts, will return empty if so
virtual ScriptInstance *instance_create(Object *p_this);
+ virtual PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this);
virtual bool instance_has(const Object *p_this) const;
virtual bool has_source_code() const;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 5f521c682a..ae67521749 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -139,17 +139,32 @@ GDScriptDataType GDScriptCompiler::_gdtype_from_datatype(const GDScriptParser::D
result.native_type = result.script_type->get_instance_base_type();
} break;
case GDScriptParser::DataType::CLASS: {
- result.kind = GDScriptDataType::GDSCRIPT;
- if (!p_datatype.class_type->owner) {
- result.script_type = Ref<GDScript>(main_script);
- } else {
- result.script_type = class_map[p_datatype.class_type->name];
+ // Locate class by constructing the path to it and following that path
+ GDScriptParser::ClassNode *class_type = p_datatype.class_type;
+ List<StringName> names;
+ while (class_type->owner) {
+ names.push_back(class_type->name);
+ class_type = class_type->owner;
}
- result.native_type = result.script_type->get_instance_base_type();
+
+ Ref<GDScript> script = Ref<GDScript>(main_script);
+ while (names.back()) {
+ if (!script->subclasses.has(names.back()->get())) {
+ ERR_PRINT("Parser bug: Cannot locate datatype class.");
+ result.has_type = false;
+ return GDScriptDataType();
+ }
+ script = script->subclasses[names.back()->get()];
+ names.pop_back();
+ }
+
+ result.kind = GDScriptDataType::GDSCRIPT;
+ result.script_type = script;
+ result.native_type = script->get_instance_base_type();
} break;
default: {
ERR_PRINT("Parser bug: converting unresolved type.");
- result.has_type = false;
+ return GDScriptDataType();
}
}
@@ -460,12 +475,14 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.alloc_stack(slevel);
}
- switch (cn->cast_type.kind) {
- case GDScriptParser::DataType::BUILTIN: {
+ GDScriptDataType cast_type = _gdtype_from_datatype(cn->cast_type);
+
+ switch (cast_type.kind) {
+ case GDScriptDataType::BUILTIN: {
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_BUILTIN);
codegen.opcodes.push_back(cn->cast_type.builtin_type);
} break;
- case GDScriptParser::DataType::NATIVE: {
+ case GDScriptDataType::NATIVE: {
int class_idx;
if (GDScriptLanguage::get_singleton()->get_global_map().has(cn->cast_type.native_type)) {
@@ -478,32 +495,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_NATIVE); // perform operator
codegen.opcodes.push_back(class_idx); // variable type
} break;
- case GDScriptParser::DataType::CLASS: {
-
- Variant script;
- int idx = -1;
- if (!cn->cast_type.class_type->owner) {
- script = codegen.script;
- } else {
- StringName name = cn->cast_type.class_type->name;
- if (codegen.script->subclasses.has(name) && class_map[name] == codegen.script->subclasses[name]) {
- idx = codegen.get_name_map_pos(name);
- idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
- } else {
- script = class_map[name];
- }
- }
-
- if (idx < 0) {
- idx = codegen.get_constant_pos(script);
- idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_CAST_TO_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- } break;
- case GDScriptParser::DataType::SCRIPT:
- case GDScriptParser::DataType::GDSCRIPT: {
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
Variant script = cn->cast_type.script_type;
int idx = codegen.get_constant_pos(script);
@@ -1149,18 +1142,18 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
if (src_address_b < 0)
return -1;
- GDScriptParser::DataType assign_type = on->arguments[0]->get_datatype();
+ GDScriptDataType assign_type = _gdtype_from_datatype(on->arguments[0]->get_datatype());
if (assign_type.has_type && !on->arguments[1]->get_datatype().has_type) {
// Typed assignment
switch (assign_type.kind) {
- case GDScriptParser::DataType::BUILTIN: {
+ case GDScriptDataType::BUILTIN: {
codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN); // perform operator
codegen.opcodes.push_back(assign_type.builtin_type); // variable type
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2
} break;
- case GDScriptParser::DataType::NATIVE: {
+ case GDScriptDataType::NATIVE: {
int class_idx;
if (GDScriptLanguage::get_singleton()->get_global_map().has(assign_type.native_type)) {
@@ -1175,34 +1168,8 @@ int GDScriptCompiler::_parse_expression(CodeGen &codegen, const GDScriptParser::
codegen.opcodes.push_back(dst_address_a); // argument 1
codegen.opcodes.push_back(src_address_b); // argument 2
} break;
- case GDScriptParser::DataType::CLASS: {
-
- Variant script;
- int idx = -1;
- if (!assign_type.class_type->owner) {
- script = codegen.script;
- } else {
- StringName name = assign_type.class_type->name;
- if (codegen.script->subclasses.has(name) && class_map[name] == codegen.script->subclasses[name]) {
- idx = codegen.get_name_map_pos(name);
- idx |= GDScriptFunction::ADDR_TYPE_CLASS_CONSTANT << GDScriptFunction::ADDR_BITS;
- } else {
- script = class_map[name];
- }
- }
-
- if (idx < 0) {
- idx = codegen.get_constant_pos(script);
- idx |= GDScriptFunction::ADDR_TYPE_LOCAL_CONSTANT << GDScriptFunction::ADDR_BITS; //make it a local constant (faster access)
- }
-
- codegen.opcodes.push_back(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT); // perform operator
- codegen.opcodes.push_back(idx); // variable type
- codegen.opcodes.push_back(dst_address_a); // argument 1
- codegen.opcodes.push_back(src_address_b); // argument 2
- } break;
- case GDScriptParser::DataType::SCRIPT:
- case GDScriptParser::DataType::GDSCRIPT: {
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
Variant script = assign_type.script_type;
int idx = codegen.get_constant_pos(script);
@@ -1845,22 +1812,21 @@ Error GDScriptCompiler::_parse_function(GDScript *p_script, const GDScriptParser
return OK;
}
-Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+
+ parsing_classes.insert(p_script);
if (p_class->owner && p_class->owner->owner) {
// Owner is not root
- StringName owner_name = p_class->owner->name;
- if (!parsed_classes.has(owner_name)) {
- if (parsing_classes.has(owner_name)) {
- _set_error("Cyclic class reference for '" + String(owner_name) + "'.", p_class);
+ if (!parsed_classes.has(p_script->_owner)) {
+ if (parsing_classes.has(p_script->_owner)) {
+ _set_error("Cyclic class reference for '" + String(p_class->name) + "'.", p_class);
return ERR_PARSE_ERROR;
}
- parsing_classes.insert(owner_name);
- Error err = _parse_class_level(class_map[owner_name].ptr(), class_map[owner_name]->_owner, p_class->owner, p_keep_state);
+ Error err = _parse_class_level(p_script->_owner, p_class->owner, p_keep_state);
if (err) {
return err;
}
- parsing_classes.erase(owner_name);
}
}
@@ -1878,47 +1844,26 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->_signals.clear();
p_script->initializer = NULL;
- p_script->subclasses.clear();
- p_script->_owner = p_owner;
p_script->tool = p_class->tool;
p_script->name = p_class->name;
Ref<GDScriptNativeClass> native;
+ GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type);
// Inheritance
- switch (p_class->base_type.kind) {
- case GDScriptParser::DataType::CLASS: {
- StringName base_name = p_class->base_type.class_type->name;
- // Make sure dependency is parsed first
- if (!parsed_classes.has(base_name)) {
- if (parsing_classes.has(base_name)) {
- _set_error("Cyclic class reference for '" + String(base_name) + "'.", p_class);
- return ERR_PARSE_ERROR;
- }
- parsing_classes.insert(base_name);
- Error err = _parse_class_level(class_map[base_name].ptr(), class_map[base_name]->_owner, p_class->base_type.class_type, p_keep_state);
- if (err) {
- return err;
- }
- parsing_classes.erase(base_name);
- }
- Ref<GDScript> base = class_map[base_name];
- p_script->base = base;
- p_script->_base = p_script->base.ptr();
- p_script->member_indices = base->member_indices;
- } break;
- case GDScriptParser::DataType::GDSCRIPT: {
- Ref<GDScript> base = p_class->base_type.script_type;
- p_script->base = base;
- p_script->_base = p_script->base.ptr();
- p_script->member_indices = base->member_indices;
- } break;
- case GDScriptParser::DataType::NATIVE: {
- int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_class->base_type.native_type];
+ switch (base_type.kind) {
+ case GDScriptDataType::NATIVE: {
+ int native_idx = GDScriptLanguage::get_singleton()->get_global_map()[base_type.native_type];
native = GDScriptLanguage::get_singleton()->get_global_array()[native_idx];
ERR_FAIL_COND_V(native.is_null(), ERR_BUG);
p_script->native = native;
} break;
+ case GDScriptDataType::GDSCRIPT: {
+ Ref<GDScript> base = base_type.script_type;
+ p_script->base = base;
+ p_script->_base = base.ptr();
+ p_script->member_indices = base->member_indices;
+ } break;
default: {
_set_error("Parser bug: invalid inheritance.", p_class);
return ERR_BUG;
@@ -2012,24 +1957,19 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
p_script->_signals[name] = p_class->_signals[i].arguments;
}
- if (p_class->owner) {
- parsed_classes.insert(p_class->name);
- if (parsing_classes.has(p_class->name)) {
- parsing_classes.erase(p_class->name);
- }
- }
+ parsed_classes.insert(p_script);
+ parsing_classes.erase(p_script);
//parse sub-classes
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
- Ref<GDScript> subclass = class_map[name];
+ GDScript *subclass = p_script->subclasses[name].ptr();
// Subclass might still be parsing, just skip it
- if (!parsed_classes.has(name) && !parsing_classes.has(name)) {
- parsing_classes.insert(name);
- Error err = _parse_class_level(subclass.ptr(), p_script, p_class->subclasses[i], p_keep_state);
+ if (!parsed_classes.has(subclass) && !parsing_classes.has(subclass)) {
+ Error err = _parse_class_level(subclass, p_class->subclasses[i], p_keep_state);
if (err)
return err;
}
@@ -2040,7 +1980,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, GDScript *p_owner
#endif
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
- p_script->subclasses.insert(name, subclass);
}
return OK;
@@ -2139,9 +2078,9 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
- Ref<GDScript> subclass = class_map[name];
+ GDScript *subclass = p_script->subclasses[name].ptr();
- Error err = _parse_class_blocks(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+ Error err = _parse_class_blocks(subclass, p_class->subclasses[i], p_keep_state);
if (err) {
return err;
}
@@ -2151,7 +2090,7 @@ Error GDScriptCompiler::_parse_class_blocks(GDScript *p_script, const GDScriptPa
return OK;
}
-void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
+void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state) {
Map<StringName, Ref<GDScript> > old_subclasses;
@@ -2159,6 +2098,8 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar
old_subclasses = p_script->subclasses;
}
+ p_script->subclasses.clear();
+
for (int i = 0; i < p_class->subclasses.size(); i++) {
StringName name = p_class->subclasses[i]->name;
@@ -2170,10 +2111,10 @@ void GDScriptCompiler::_make_scripts(const GDScript *p_script, const GDScriptPar
subclass.instance();
}
- subclass->_owner = const_cast<GDScript *>(p_script);
- class_map.insert(name, subclass);
+ subclass->_owner = p_script;
+ p_script->subclasses.insert(name, subclass);
- _make_scripts(subclass.ptr(), p_class->subclasses[i], p_keep_state);
+ _make_scripts(subclass.ptr(), p_class->subclasses[i], false);
}
}
@@ -2192,7 +2133,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
// Create scripts for subclasses beforehand so they can be referenced
_make_scripts(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
- Error err = _parse_class_level(p_script, NULL, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
+ p_script->_owner = NULL;
+ Error err = _parse_class_level(p_script, static_cast<const GDScriptParser::ClassNode *>(root), p_keep_state);
if (err)
return err;
diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h
index 8440807a56..2cf630ba72 100644
--- a/modules/gdscript/gdscript_compiler.h
+++ b/modules/gdscript/gdscript_compiler.h
@@ -38,9 +38,8 @@
class GDScriptCompiler {
const GDScriptParser *parser;
- Map<StringName, Ref<GDScript> > class_map;
- Set<StringName> parsed_classes;
- Set<StringName> parsing_classes;
+ Set<GDScript *> parsed_classes;
+ Set<GDScript *> parsing_classes;
GDScript *main_script;
struct CodeGen {
@@ -149,9 +148,9 @@ class GDScriptCompiler {
int _parse_expression(CodeGen &codegen, const GDScriptParser::Node *p_expression, int p_stack_level, bool p_root = false, bool p_initializer = false);
Error _parse_block(CodeGen &codegen, const GDScriptParser::BlockNode *p_block, int p_stack_level = 0, int p_break_addr = -1, int p_continue_addr = -1);
Error _parse_function(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::FunctionNode *p_func, bool p_for_ready = false);
- Error _parse_class_level(GDScript *p_script, GDScript *p_owner, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ Error _parse_class_level(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
Error _parse_class_blocks(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
- void _make_scripts(const GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
+ void _make_scripts(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state);
int err_line;
int err_column;
StringName source;
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 890bea0d1d..a6b5c1535b 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("<para>");
+
+ List<String> 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<String> lines = text.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0)
+ xml_output.append("<para>");
+
+ xml_output.append(lines[i].xml_escape());
+
+ if (i != lines.size() - 1)
+ xml_output.append("</para>\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<String> lines = text.split("\n");
+ for (int i = 0; i < lines.size(); i++) {
+ if (i != 0)
+ xml_output.append("<para>");
+
+ xml_output.append(lines[i].xml_escape());
+
+ if (i != lines.size() - 1)
+ xml_output.append("</para>\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("</a>");
+ } else if (tag == "/code") {
+ xml_output.append("</c>");
+ } else if (tag == "/codeblock") {
+ xml_output.append("</code>");
+ }
+ } 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<String> 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("<c>");
+ xml_output.append(tag);
+ xml_output.append("</c>");
+
+ 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("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else {
+ const MethodInterface *target_imethod = target_itype->find_method_by_name(target_cname);
+
+ if (target_imethod) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->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("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else {
+ const PropertyInterface *target_iprop = target_itype->find_property_by_name(target_cname);
+
+ if (target_iprop) {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->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("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ } else if (link_tag == "enum") {
+ StringName search_cname = !target_itype ? target_cname :
+ StringName(target_itype->name + "." + (String)target_cname);
+
+ const Map<StringName, TypeInterface>::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("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_enum_itype.proxy_name); // Includes nesting class if any
+ xml_output.append("\"/>");
+ } else {
+ ERR_PRINTS("Cannot resolve enum reference in documentation: " + link_target);
+
+ xml_output.append("<c>");
+ xml_output.append(link_target);
+ xml_output.append("</c>");
+ }
+ }
+
+ pos = brk_end + 1;
+ } else if (doc->class_list.has(tag)) {
+ if (tag == "Array" || tag == "Dictionary") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE_COLLECTIONS ".");
+ xml_output.append(tag);
+ xml_output.append("\"/>");
+ } else if (tag == "bool" || tag == "int") {
+ xml_output.append("<see cref=\"");
+ xml_output.append(tag);
+ xml_output.append("\"/>");
+ } else if (tag == "float") {
+ xml_output.append("<see cref=\""
+#ifdef REAL_T_IS_DOUBLE
+ "double"
+#else
+ "float"
+#endif
+ "\"/>");
+ } else if (tag == "Variant") {
+ // We use System.Object for Variant, so there is no Variant type in C#
+ xml_output.append("<c>Variant</c>");
+ } else if (tag == "String") {
+ xml_output.append("<see cref=\"string\"/>");
+ } else if (tag == "Nil") {
+ xml_output.append("<see langword=\"null\"/>");
+ } else if (tag.begins_with("@")) {
+ // @Global Scope, @GDScript, etc
+ xml_output.append("<c>");
+ xml_output.append(tag);
+ xml_output.append("</c>");
+ } else if (tag == "PoolByteArray") {
+ xml_output.append("<see cref=\"byte\"/>");
+ } else if (tag == "PoolIntArray") {
+ xml_output.append("<see cref=\"int\"/>");
+ } else if (tag == "PoolRealArray") {
+#ifdef REAL_T_IS_DOUBLE
+ xml_output.append("<see cref=\"double\"/>");
+#else
+ xml_output.append("<see cref=\"float\"/>");
+#endif
+ } else if (tag == "PoolStringArray") {
+ xml_output.append("<see cref=\"string\"/>");
+ } else if (tag == "PoolVector2Array") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector2\"/>");
+ } else if (tag == "PoolVector3Array") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Vector3\"/>");
+ } else if (tag == "PoolColorArray") {
+ xml_output.append("<see cref=\"" BINDINGS_NAMESPACE ".Color\"/>");
+ } 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("<see cref=\"" BINDINGS_NAMESPACE ".");
+ xml_output.append(target_itype->proxy_name);
+ xml_output.append("\"/>");
+ } else {
+ ERR_PRINTS("Cannot resolve type reference in documentation: " + tag);
+
+ xml_output.append("<c>");
+ xml_output.append(tag);
+ xml_output.append("</c>");
+ }
+ }
+
+ 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("<c>");
+
+ code_tag = true;
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
+ } else if (tag == "codeblock") {
+ xml_output.append("<code>");
+
+ 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 <para> 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("<a href=\"");
+ xml_output.append(url);
+ 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("<a href=\"");
+ xml_output.append(url);
+ 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("</para>");
+
+ 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<String> &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<String> &p_output) {
const ConstantInterface &iconstant = E->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- p_output.push_back(MEMBER_BEGIN "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\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 "/// </summary>");
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
}
p_output.push_back(MEMBER_BEGIN "public const int ");
@@ -369,20 +743,20 @@ void BindingsGenerator::_generate_global_constants(List<String> &p_output) {
const ConstantInterface &iconstant = F->get();
if (iconstant.const_doc && iconstant.const_doc->description.size()) {
- p_output.push_back(INDENT2 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), NULL);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ p_output.push_back(INDENT2 "/// <summary>\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 "/// </summary>\n");
+ p_output.push_back(INDENT2 "/// </summary>\n");
+ }
}
p_output.push_back(INDENT2);
@@ -399,6 +773,8 @@ void BindingsGenerator::_generate_global_constants(List<String> &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 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(class_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = class_doc->description.split("\n");
+ if (summary_lines.size()) {
+ output.push_back(INDENT1 "/// <summary>\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 "/// </summary>\n");
+ output.push_back(INDENT1 "/// </summary>\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 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ output.push_back(MEMBER_BEGIN "/// <summary>\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 "/// </summary>");
+ output.push_back(INDENT2 "/// </summary>");
+ }
}
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 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(iconstant.const_doc->description), &itype);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = iconstant.const_doc->description.split("\n");
+ if (summary_lines.size()) {
+ output.push_back(INDENT3 "/// <summary>\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 "/// </summary>\n");
+ output.push_back(INDENT3 "/// </summary>\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 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(p_iprop.prop_doc->description), &p_itype);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = p_iprop.prop_doc->description.split("\n");
+ if (summary_lines.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\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 "/// </summary>");
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
}
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 "/// <param name=\"" + iarg.name + "\">If the param is null, then the default value is " + def_arg + "</param>\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 "/// <param name=\"" + param_tag_name + "\">If the parameter is null, then the default value is " + def_arg + "</param>\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 "/// <summary>\n");
+ String xml_summary = bbcode_to_xml(fix_doc_description(p_imethod.method_doc->description), &p_itype);
+ Vector<String> summary_lines = xml_summary.split("\n");
- Vector<String> description_lines = p_imethod.method_doc->description.split("\n");
+ if (summary_lines.size() || default_args_doc.size()) {
+ p_output.push_back(MEMBER_BEGIN "/// <summary>\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<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
- p_output.push_back(E->get().xml_escape());
- }
+ for (List<String>::Element *E = default_args_doc.front(); E; E = E->next()) {
+ p_output.push_back(E->get());
+ }
- p_output.push_back(INDENT2 "/// </summary>");
+ p_output.push_back(INDENT2 "/// </summary>");
+ }
}
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 8a1c942f6b..7eaebeabbd 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<PropertyInterface>::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<PropertyInterface>::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);
diff --git a/modules/mono/editor/script_class_parser.cpp b/modules/mono/editor/script_class_parser.cpp
index 6b2ec5cc20..dfb652a7aa 100644
--- a/modules/mono/editor/script_class_parser.cpp
+++ b/modules/mono/editor/script_class_parser.cpp
@@ -266,6 +266,20 @@ Error ScriptClassParser::_skip_generic_type_params() {
if (tk == TK_IDENTIFIER) {
tk = get_token();
+ // Type specifications can end with "?" to denote nullable types, such as IList<int?>
+ if (tk == TK_SYMBOL) {
+ tk = get_token();
+ if (value.operator String() != "?") {
+ error_str = "Expected " + get_token_name(TK_IDENTIFIER) + ", found unexpected symbol '" + value + "'";
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ if (tk != TK_OP_GREATER && tk != TK_COMMA) {
+ error_str = "Nullable type symbol '?' is only allowed after an identifier, but found " + get_token_name(tk) + " next.";
+ error = true;
+ return ERR_PARSE_ERROR;
+ }
+ }
if (tk == TK_PERIOD) {
while (true) {
diff --git a/modules/mono/utils/mono_reg_utils.cpp b/modules/mono/utils/mono_reg_utils.cpp
index 0eb4b3b8b3..d7f9b22c31 100644
--- a/modules/mono/utils/mono_reg_utils.cpp
+++ b/modules/mono/utils/mono_reg_utils.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "mono_reg_utils.h"
+#include "core/os/dir_access.h"
#ifdef WINDOWS_ENABLED
@@ -200,6 +201,13 @@ String find_msbuild_tools_path() {
val += "\\";
}
+ // Since VS2019, the directory is simply named "Current"
+ String msBuildDirectory = val + "MSBuild\\Current\\Bin";
+ if (DirAccess::exists(msBuildDirectory)) {
+ return msBuildDirectory;
+ }
+
+ // Directory name "15.0" is used in VS 2017
return val + "MSBuild\\15.0\\Bin";
}
}