From d0e7d9b62f0bcc2ba438b12c8bfbf68d82fff0ea Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Sun, 29 Nov 2020 08:07:57 +0530 Subject: Documentation generation for GDScript - ClassDoc added to GDScript and property reflection data were extracted from parse tree - GDScript comments are collected from tokenizer for documentation and applied to the ClassDoc by the GDScript compiler - private docs were excluded (name with underscore prefix and doesn't have any doc comments) - default values (of non exported vars), arguments are extraced from the parser - Integrated with GDScript 2.0 and new enums were added. - merge conflicts fixed --- modules/gdscript/gdscript.cpp | 259 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 250 insertions(+), 9 deletions(-) (limited to 'modules/gdscript/gdscript.cpp') diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 53602f7a9b..8524ec276a 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -39,6 +39,8 @@ #include "core/io/file_access_encrypted.h" #include "core/os/file_access.h" #include "core/os/os.h" +#include "editor/editor_help.h" +#include "editor/plugins/script_editor_plugin.h" #include "gdscript_analyzer.h" #include "gdscript_cache.h" #include "gdscript_compiler.h" @@ -231,7 +233,7 @@ void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { } #endif -void GDScript::get_script_method_list(List *p_list) const { +void GDScript::_get_script_method_list(List *r_list, bool p_include_base) const { const GDScript *current = this; while (current) { for (const Map::Element *E = current->member_functions.front(); E; E = E->next()) { @@ -239,18 +241,29 @@ void GDScript::get_script_method_list(List *p_list) const { MethodInfo mi; mi.name = E->key(); for (int i = 0; i < func->get_argument_count(); i++) { - mi.arguments.push_back(func->get_argument_type(i)); + PropertyInfo arginfo = func->get_argument_type(i); +#if TOOLS_ENABLED + arginfo.name = func->get_argument_name(i); +#endif + mi.arguments.push_back(arginfo); } mi.return_val = func->get_return_type(); - p_list->push_back(mi); + r_list->push_back(mi); + } + if (!p_include_base) { + return; } current = current->_base; } } -void GDScript::get_script_property_list(List *p_list) const { +void GDScript::get_script_method_list(List *r_list) const { + _get_script_method_list(r_list, true); +} + +void GDScript::_get_script_property_list(List *r_list, bool p_include_base) const { const GDScript *sptr = this; List props; @@ -269,15 +282,22 @@ void GDScript::get_script_property_list(List *p_list) const { for (int i = 0; i < msort.size(); i++) { props.push_front(sptr->member_info[msort[i].name]); } + if (!p_include_base) { + break; + } sptr = sptr->_base; } for (List::Element *E = props.front(); E; E = E->next()) { - p_list->push_back(E->get()); + r_list->push_back(E->get()); } } +void GDScript::get_script_property_list(List *r_list) const { + _get_script_property_list(r_list, true); +} + bool GDScript::has_method(const StringName &p_method) const { return member_functions.has(p_method); } @@ -383,6 +403,193 @@ void GDScript::_update_exports_values(Map &values, Listget()); } } + +void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) { + if (_owner) { + _owner->_add_doc(p_inner_class); + } else { + for (int i = 0; i < docs.size(); i++) { + if (docs[i].name == p_inner_class.name) { + docs.remove(i); + break; + } + } + docs.append(p_inner_class); + } +} + +void GDScript::_clear_doc() { + if (EditorHelp::get_doc_data() && EditorHelp::get_doc_data()->has_doc(doc.name)) { + EditorHelp::get_doc_data()->remove_doc(doc.name); + doc = DocData::ClassDoc(); + } + docs.clear(); +} + +void GDScript::_update_doc() { + _clear_doc(); + + doc.script_path = "\"" + get_path().get_slice("://", 1) + "\""; + if (!name.empty()) { + doc.name = name; + } else { + doc.name = doc.script_path; + } + + if (_owner) { + doc.name = _owner->doc.name + "." + doc.name; + doc.script_path = doc.script_path + "." + doc.name; + } + + doc.is_script_doc = true; + + if (base.is_valid() && base->is_valid()) { + if (base->doc.name != String()) { + doc.inherits = base->doc.name; + } else { + doc.inherits = base->get_instance_base_type(); + } + } else if (native.is_valid()) { + doc.inherits = native->get_name(); + } + + doc.brief_description = doc_brief_description; + doc.description = doc_description; + doc.tutorials = doc_tutorials; + + for (Map::Element *E = doc_enums.front(); E; E = E->next()) { + if (E->value().description != "") { + doc.enums[E->key()] = E->value().description; + } + } + + List methods; + _get_script_method_list(&methods, false); + for (int i = 0; i < methods.size(); i++) { + // Ignore internal methods. + if (methods[i].name[0] == '@') { + continue; + } + + DocData::MethodDoc method_doc; + const String &class_name = methods[i].name; + if (member_functions.has(class_name)) { + GDScriptFunction *fn = member_functions[class_name]; + + // Change class name if return type is script reference. + GDScriptDataType return_type = fn->get_return_type(); + if (return_type.kind == GDScriptDataType::GDSCRIPT) { + methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to(return_type.script_type)); + } + + // Change class name if argumetn is script reference. + for (int j = 0; j < fn->get_argument_count(); j++) { + GDScriptDataType arg_type = fn->get_argument_type(j); + if (arg_type.kind == GDScriptDataType::GDSCRIPT) { + methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to(arg_type.script_type)); + } + } + } + if (doc_functions.has(methods[i].name)) { + DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]); + } else { + DocData::method_doc_from_methodinfo(method_doc, methods[i], String()); + } + doc.methods.push_back(method_doc); + } + + List props; + _get_script_property_list(&props, false); + for (int i = 0; i < props.size(); i++) { + ScriptMemberInfo scr_member_info; + scr_member_info.propinfo = props[i]; + scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + if (member_indices.has(props[i].name)) { + const MemberInfo &mi = member_indices[props[i].name]; + scr_member_info.setter = mi.setter; + scr_member_info.getter = mi.getter; + if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) { + scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name( + Object::cast_to(mi.data_type.script_type)); + } + } + if (member_default_values.has(props[i].name)) { + scr_member_info.has_default_value = true; + scr_member_info.default_value = member_default_values[props[i].name]; + } + if (doc_variables.has(props[i].name)) { + scr_member_info.doc_string = doc_variables[props[i].name]; + } + + DocData::PropertyDoc prop_doc; + DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info); + doc.properties.push_back(prop_doc); + } + + List signals; + _get_script_signal_list(&signals, false); + for (int i = 0; i < signals.size(); i++) { + DocData::MethodDoc signal_doc; + if (doc_signals.has(signals[i].name)) { + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name); + } else { + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String()); + } + doc.signals.push_back(signal_doc); + } + + for (Map::Element *E = constants.front(); E; E = E->next()) { + if (subclasses.has(E->key())) { + continue; + } + + // Enums. + bool is_enum = false; + if (E->value().get_type() == Variant::DICTIONARY) { + if (doc_enums.has(E->key())) { + is_enum = true; + for (int i = 0; i < doc_enums[E->key()].values.size(); i++) { + doc_enums[E->key()].values.write[i].enumeration = E->key(); + doc.constants.push_back(doc_enums[E->key()].values[i]); + } + } + } + if (!is_enum && doc_enums.has("@unnamed_enums")) { + for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) { + if (E->key() == doc_enums["@unnamed_enums"].values[i].name) { + is_enum = true; + DocData::ConstantDoc constant_doc; + constant_doc.enumeration = "@unnamed_enums"; + DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description); + doc.constants.push_back(constant_doc); + break; + } + } + } + if (!is_enum) { + DocData::ConstantDoc constant_doc; + String doc_description; + if (doc_constants.has(E->key())) { + doc_description = doc_constants[E->key()]; + } + DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description); + doc.constants.push_back(constant_doc); + } + } + + for (Map>::Element *E = subclasses.front(); E; E = E->next()) { + E->get()->_update_doc(); + } + + if (EditorHelp::get_doc_data()) { + EditorHelp::get_doc_data()->add_doc(doc); + } + if (ScriptEditor::get_singleton()) { + ScriptEditor::get_singleton()->update_doc(doc.name); + } + + _add_doc(doc); +} #endif bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) { @@ -638,6 +845,10 @@ Error GDScript::reload(bool p_keep_state) { GDScriptCompiler compiler; err = compiler.compile(&parser, this, p_keep_state); +#if TOOLS_ENABLED + this->_update_doc(); +#endif + if (err) { if (can_run) { if (EngineDebugger::is_active()) { @@ -911,7 +1122,7 @@ bool GDScript::has_script_signal(const StringName &p_signal) const { return false; } -void GDScript::get_script_signal_list(List *r_signals) const { +void GDScript::_get_script_signal_list(List *r_list, bool p_include_base) const { for (const Map>::Element *E = _signals.front(); E; E = E->next()) { MethodInfo mi; mi.name = E->key(); @@ -920,20 +1131,43 @@ void GDScript::get_script_signal_list(List *r_signals) const { arg.name = E->get()[i]; mi.arguments.push_back(arg); } - r_signals->push_back(mi); + r_list->push_back(mi); + } + + if (!p_include_base) { + return; } if (base.is_valid()) { - base->get_script_signal_list(r_signals); + base->get_script_signal_list(r_list); } #ifdef TOOLS_ENABLED else if (base_cache.is_valid()) { - base_cache->get_script_signal_list(r_signals); + base_cache->get_script_signal_list(r_list); } #endif } +void GDScript::get_script_signal_list(List *r_signals) const { + _get_script_signal_list(r_signals, true); +} + +String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) { + ERR_FAIL_NULL_V(p_gdscript, String()); + + String class_name; + while (p_gdscript) { + if (class_name == "") { + class_name = p_gdscript->get_script_class_name(); + } else { + class_name = p_gdscript->get_script_class_name() + "." + class_name; + } + p_gdscript = p_gdscript->_owner; + } + return class_name; +} + GDScript::GDScript() : script_list(this) { valid = false; @@ -1061,6 +1295,13 @@ GDScript::~GDScript() { _save_orphaned_subclasses(); +#ifdef TOOLS_ENABLED + // Clearing inner class doc, script doc only cleared when the script source deleted. + if (_owner) { + _clear_doc(); + } +#endif + #ifdef DEBUG_ENABLED { MutexLock lock(GDScriptLanguage::get_singleton()->lock); -- cgit v1.2.3 From 42bfa169960b59c5d9337e9f63862f5feae92d58 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Sun, 29 Nov 2020 09:12:06 +0530 Subject: Refactor DocData into core and editor (DocTools) parts --- modules/gdscript/gdscript.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'modules/gdscript/gdscript.cpp') diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 8524ec276a..b00eb6f75b 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -39,8 +39,6 @@ #include "core/io/file_access_encrypted.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "editor/editor_help.h" -#include "editor/plugins/script_editor_plugin.h" #include "gdscript_analyzer.h" #include "gdscript_cache.h" #include "gdscript_compiler.h" @@ -242,7 +240,7 @@ void GDScript::_get_script_method_list(List *r_list, bool p_include_ mi.name = E->key(); for (int i = 0; i < func->get_argument_count(); i++) { PropertyInfo arginfo = func->get_argument_type(i); -#if TOOLS_ENABLED +#ifdef TOOLS_ENABLED arginfo.name = func->get_argument_name(i); #endif mi.arguments.push_back(arginfo); @@ -419,11 +417,8 @@ void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) { } void GDScript::_clear_doc() { - if (EditorHelp::get_doc_data() && EditorHelp::get_doc_data()->has_doc(doc.name)) { - EditorHelp::get_doc_data()->remove_doc(doc.name); - doc = DocData::ClassDoc(); - } docs.clear(); + doc = DocData::ClassDoc(); } void GDScript::_update_doc() { @@ -581,13 +576,6 @@ void GDScript::_update_doc() { E->get()->_update_doc(); } - if (EditorHelp::get_doc_data()) { - EditorHelp::get_doc_data()->add_doc(doc); - } - if (ScriptEditor::get_singleton()) { - ScriptEditor::get_singleton()->update_doc(doc.name); - } - _add_doc(doc); } #endif @@ -845,8 +833,8 @@ Error GDScript::reload(bool p_keep_state) { GDScriptCompiler compiler; err = compiler.compile(&parser, this, p_keep_state); -#if TOOLS_ENABLED - this->_update_doc(); +#ifdef TOOLS_ENABLED + _update_doc(); #endif if (err) { -- cgit v1.2.3