You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-11 13:10:58 +00:00
Improve and fix GDScript documentation generation & behavior
Removes documentation generation (docgen) from the GDScript compiler to its own file. Adds support for GDScript enums and signal parameters and quite a few other assorted fixes and improvements.
This commit is contained in:
271
modules/gdscript/editor/gdscript_docgen.cpp
Normal file
271
modules/gdscript/editor/gdscript_docgen.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/**************************************************************************/
|
||||
/* gdscript_docgen.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#include "gdscript_docgen.h"
|
||||
#include "../gdscript.h"
|
||||
|
||||
using GDP = GDScriptParser;
|
||||
using GDType = GDP::DataType;
|
||||
|
||||
static String _get_script_path(const String &p_path) {
|
||||
return vformat(R"("%s")", p_path.get_slice("://", 1));
|
||||
}
|
||||
|
||||
static String _get_class_name(const GDP::ClassNode &p_class) {
|
||||
const GDP::ClassNode *curr_class = &p_class;
|
||||
if (!curr_class->identifier) { // All inner classes have a identifier, so this is the outer class
|
||||
return _get_script_path(curr_class->fqcn);
|
||||
}
|
||||
|
||||
String full_name = curr_class->identifier->name;
|
||||
while (curr_class->outer) {
|
||||
curr_class = curr_class->outer;
|
||||
if (!curr_class->identifier) { // All inner classes have a identifier, so this is the outer class
|
||||
return vformat("%s.%s", _get_script_path(curr_class->fqcn), full_name);
|
||||
}
|
||||
full_name = vformat("%s.%s", curr_class->identifier->name, full_name);
|
||||
}
|
||||
return full_name;
|
||||
}
|
||||
|
||||
static PropertyInfo _property_info_from_datatype(const GDType &p_type) {
|
||||
PropertyInfo pi;
|
||||
pi.type = p_type.builtin_type;
|
||||
if (p_type.kind == GDType::CLASS) {
|
||||
pi.class_name = _get_class_name(*p_type.class_type);
|
||||
} else if (p_type.kind == GDType::ENUM && p_type.enum_type != StringName()) {
|
||||
pi.type = Variant::INT; // Only int types are recognized as enums by the EditorHelp
|
||||
pi.usage |= PROPERTY_USAGE_CLASS_IS_ENUM;
|
||||
// Replace :: from enum's use of fully qualified class names with regular .
|
||||
pi.class_name = String(p_type.native_type).replace("::", ".");
|
||||
} else if (p_type.kind == GDType::NATIVE) {
|
||||
pi.class_name = p_type.native_type;
|
||||
}
|
||||
return pi;
|
||||
}
|
||||
|
||||
void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_class) {
|
||||
p_script->_clear_doc();
|
||||
|
||||
DocData::ClassDoc &doc = p_script->doc;
|
||||
|
||||
doc.script_path = _get_script_path(p_script->get_script_path());
|
||||
if (p_script->name.is_empty()) {
|
||||
doc.name = doc.script_path;
|
||||
} else {
|
||||
doc.name = p_script->name;
|
||||
}
|
||||
|
||||
if (p_script->_owner) {
|
||||
doc.name = p_script->_owner->doc.name + "." + doc.name;
|
||||
doc.script_path = doc.script_path + "." + doc.name;
|
||||
}
|
||||
|
||||
doc.is_script_doc = true;
|
||||
|
||||
if (p_script->base.is_valid() && p_script->base->is_valid()) {
|
||||
if (!p_script->base->doc.name.is_empty()) {
|
||||
doc.inherits = p_script->base->doc.name;
|
||||
} else {
|
||||
doc.inherits = p_script->base->get_instance_base_type();
|
||||
}
|
||||
} else if (p_script->native.is_valid()) {
|
||||
doc.inherits = p_script->native->get_name();
|
||||
}
|
||||
|
||||
doc.brief_description = p_class->doc_brief_description;
|
||||
doc.description = p_class->doc_description;
|
||||
for (const Pair<String, String> &p : p_class->doc_tutorials) {
|
||||
DocData::TutorialDoc td;
|
||||
td.title = p.first;
|
||||
td.link = p.second;
|
||||
doc.tutorials.append(td);
|
||||
}
|
||||
|
||||
for (const GDP::ClassNode::Member &member : p_class->members) {
|
||||
switch (member.type) {
|
||||
case GDP::ClassNode::Member::CLASS: {
|
||||
const GDP::ClassNode *inner_class = member.m_class;
|
||||
const StringName &class_name = inner_class->identifier->name;
|
||||
|
||||
p_script->member_lines[class_name] = inner_class->start_line;
|
||||
|
||||
// Recursively generate inner class docs
|
||||
// Needs inner GDScripts to exist: previously generated in GDScriptCompiler::make_scripts()
|
||||
GDScriptDocGen::generate_docs(*p_script->subclasses[class_name], inner_class);
|
||||
} break;
|
||||
|
||||
case GDP::ClassNode::Member::CONSTANT: {
|
||||
const GDP::ConstantNode *m_const = member.constant;
|
||||
const StringName &const_name = member.constant->identifier->name;
|
||||
|
||||
p_script->member_lines[const_name] = m_const->start_line;
|
||||
|
||||
DocData::ConstantDoc const_doc;
|
||||
DocData::constant_doc_from_variant(const_doc, const_name, m_const->initializer->reduced_value, m_const->doc_description);
|
||||
doc.constants.push_back(const_doc);
|
||||
} break;
|
||||
|
||||
case GDP::ClassNode::Member::FUNCTION: {
|
||||
const GDP::FunctionNode *m_func = member.function;
|
||||
const StringName &func_name = m_func->identifier->name;
|
||||
|
||||
p_script->member_lines[func_name] = m_func->start_line;
|
||||
|
||||
MethodInfo mi;
|
||||
mi.name = func_name;
|
||||
|
||||
if (m_func->return_type) {
|
||||
mi.return_val = _property_info_from_datatype(m_func->return_type->get_datatype());
|
||||
}
|
||||
for (const GDScriptParser::ParameterNode *p : m_func->parameters) {
|
||||
PropertyInfo pi = _property_info_from_datatype(p->get_datatype());
|
||||
pi.name = p->identifier->name;
|
||||
mi.arguments.push_back(pi);
|
||||
}
|
||||
|
||||
DocData::MethodDoc method_doc;
|
||||
DocData::method_doc_from_methodinfo(method_doc, mi, m_func->doc_description);
|
||||
doc.methods.push_back(method_doc);
|
||||
} break;
|
||||
|
||||
case GDP::ClassNode::Member::SIGNAL: {
|
||||
const GDP::SignalNode *m_signal = member.signal;
|
||||
const StringName &signal_name = m_signal->identifier->name;
|
||||
|
||||
p_script->member_lines[signal_name] = m_signal->start_line;
|
||||
|
||||
MethodInfo mi;
|
||||
mi.name = signal_name;
|
||||
for (const GDScriptParser::ParameterNode *p : m_signal->parameters) {
|
||||
PropertyInfo pi = _property_info_from_datatype(p->get_datatype());
|
||||
pi.name = p->identifier->name;
|
||||
mi.arguments.push_back(pi);
|
||||
}
|
||||
|
||||
DocData::MethodDoc signal_doc;
|
||||
DocData::signal_doc_from_methodinfo(signal_doc, mi, m_signal->doc_description);
|
||||
doc.signals.push_back(signal_doc);
|
||||
} break;
|
||||
|
||||
case GDP::ClassNode::Member::VARIABLE: {
|
||||
const GDP::VariableNode *m_var = member.variable;
|
||||
const StringName &var_name = m_var->identifier->name;
|
||||
|
||||
p_script->member_lines[var_name] = m_var->start_line;
|
||||
|
||||
DocData::PropertyDoc prop_doc;
|
||||
|
||||
prop_doc.name = var_name;
|
||||
prop_doc.description = m_var->doc_description;
|
||||
|
||||
GDType dt = m_var->get_datatype();
|
||||
switch (dt.kind) {
|
||||
case GDType::CLASS:
|
||||
prop_doc.type = _get_class_name(*dt.class_type);
|
||||
break;
|
||||
case GDType::VARIANT:
|
||||
prop_doc.type = "Variant";
|
||||
break;
|
||||
case GDType::ENUM:
|
||||
prop_doc.type = Variant::get_type_name(dt.builtin_type);
|
||||
// Replace :: from enum's use of fully qualified class names with regular .
|
||||
prop_doc.enumeration = String(dt.native_type).replace("::", ".");
|
||||
break;
|
||||
case GDType::NATIVE:;
|
||||
prop_doc.type = dt.native_type;
|
||||
break;
|
||||
case GDType::BUILTIN:
|
||||
prop_doc.type = Variant::get_type_name(dt.builtin_type);
|
||||
break;
|
||||
default:
|
||||
// SCRIPT: can be preload()'d and perhaps used as types directly?
|
||||
// RESOLVING & UNRESOLVED should never happen since docgen requires analyzing w/o errors
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_var->property == GDP::VariableNode::PROP_SETGET) {
|
||||
if (m_var->setter_pointer != nullptr) {
|
||||
prop_doc.setter = m_var->setter_pointer->name;
|
||||
}
|
||||
if (m_var->getter_pointer != nullptr) {
|
||||
prop_doc.getter = m_var->getter_pointer->name;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_var->initializer && m_var->initializer->is_constant) {
|
||||
prop_doc.default_value = m_var->initializer->reduced_value.get_construct_string().replace("\n", "");
|
||||
}
|
||||
|
||||
prop_doc.overridden = false;
|
||||
|
||||
doc.properties.push_back(prop_doc);
|
||||
} break;
|
||||
|
||||
case GDP::ClassNode::Member::ENUM: {
|
||||
const GDP::EnumNode *m_enum = member.m_enum;
|
||||
StringName name = m_enum->identifier->name;
|
||||
|
||||
p_script->member_lines[name] = m_enum->start_line;
|
||||
|
||||
for (const GDP::EnumNode::Value &val : m_enum->values) {
|
||||
DocData::ConstantDoc const_doc;
|
||||
const_doc.name = val.identifier->name;
|
||||
const_doc.value = String(Variant(val.value));
|
||||
const_doc.description = val.doc_description;
|
||||
const_doc.enumeration = name;
|
||||
|
||||
doc.enums[const_doc.name] = const_doc.description;
|
||||
doc.constants.push_back(const_doc);
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case GDP::ClassNode::Member::ENUM_VALUE: {
|
||||
const GDP::EnumNode::Value &m_enum_val = member.enum_value;
|
||||
const StringName &name = m_enum_val.identifier->name;
|
||||
|
||||
p_script->member_lines[name] = m_enum_val.identifier->start_line;
|
||||
|
||||
DocData::ConstantDoc constant_doc;
|
||||
constant_doc.enumeration = "@unnamed_enums";
|
||||
DocData::constant_doc_from_variant(constant_doc, name, m_enum_val.value, m_enum_val.doc_description);
|
||||
doc.constants.push_back(constant_doc);
|
||||
} break;
|
||||
case GDP::ClassNode::Member::GROUP:
|
||||
case GDP::ClassNode::Member::UNDEFINED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add doc to the outer-most class.
|
||||
p_script->_add_doc(doc);
|
||||
}
|
||||
42
modules/gdscript/editor/gdscript_docgen.h
Normal file
42
modules/gdscript/editor/gdscript_docgen.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**************************************************************************/
|
||||
/* gdscript_docgen.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#ifndef GDSCRIPT_DOCGEN_H
|
||||
#define GDSCRIPT_DOCGEN_H
|
||||
|
||||
#include "../gdscript_parser.h"
|
||||
#include "core/doc_data.h"
|
||||
|
||||
class GDScriptDocGen {
|
||||
public:
|
||||
static void generate_docs(GDScript *p_script, const GDScriptParser::ClassNode *p_class);
|
||||
};
|
||||
|
||||
#endif // GDSCRIPT_DOCGEN_H
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
#include "editor/editor_paths.h"
|
||||
#include "editor/gdscript_docgen.h"
|
||||
#endif
|
||||
|
||||
///////////////////////////
|
||||
@@ -340,12 +341,11 @@ void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_incl
|
||||
r_list->push_back(E);
|
||||
}
|
||||
|
||||
props.clear();
|
||||
|
||||
if (!p_include_base) {
|
||||
break;
|
||||
}
|
||||
|
||||
props.clear();
|
||||
sptr = sptr->_base;
|
||||
}
|
||||
}
|
||||
@@ -461,9 +461,9 @@ void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List
|
||||
}
|
||||
|
||||
void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) {
|
||||
if (_owner) {
|
||||
if (_owner) { // Only the top-level class stores doc info
|
||||
_owner->_add_doc(p_inner_class);
|
||||
} else {
|
||||
} else { // Remove old docs, add new
|
||||
for (int i = 0; i < docs.size(); i++) {
|
||||
if (docs[i].name == p_inner_class.name) {
|
||||
docs.remove_at(i);
|
||||
@@ -478,167 +478,6 @@ void GDScript::_clear_doc() {
|
||||
docs.clear();
|
||||
doc = DocData::ClassDoc();
|
||||
}
|
||||
|
||||
void GDScript::_update_doc() {
|
||||
_clear_doc();
|
||||
|
||||
doc.script_path = vformat(R"("%s")", get_script_path().get_slice("://", 1));
|
||||
if (!name.is_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.is_empty()) {
|
||||
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 (const KeyValue<String, DocData::EnumDoc> &E : doc_enums) {
|
||||
if (!E.value.description.is_empty()) {
|
||||
doc.enums[E.key] = E.value.description;
|
||||
}
|
||||
}
|
||||
|
||||
List<MethodInfo> 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<GDScript>(return_type.script_type));
|
||||
}
|
||||
|
||||
// Change class name if argument 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<GDScript>(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<PropertyInfo> props;
|
||||
_get_script_property_list(&props, false);
|
||||
for (int i = 0; i < props.size(); i++) {
|
||||
if (props[i].usage & PROPERTY_USAGE_CATEGORY || props[i].usage & PROPERTY_USAGE_GROUP || props[i].usage & PROPERTY_USAGE_SUBGROUP) {
|
||||
continue;
|
||||
}
|
||||
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<GDScript>(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<MethodInfo> 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], doc_signals[signals[i].name]);
|
||||
} else {
|
||||
DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String());
|
||||
}
|
||||
doc.signals.push_back(signal_doc);
|
||||
}
|
||||
|
||||
for (const KeyValue<StringName, Variant> &E : constants) {
|
||||
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 const_description;
|
||||
if (doc_constants.has(E.key)) {
|
||||
const_description = doc_constants[E.key];
|
||||
}
|
||||
DocData::constant_doc_from_variant(constant_doc, E.key, E.value, const_description);
|
||||
doc.constants.push_back(constant_doc);
|
||||
}
|
||||
}
|
||||
|
||||
for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
|
||||
E.value->_update_doc();
|
||||
}
|
||||
|
||||
_add_doc(doc);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) {
|
||||
@@ -905,6 +744,13 @@ Error GDScript::reload(bool p_keep_state) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Done after compilation because it needs the GDScript object's inner class GDScript objects,
|
||||
// which are made by calling make_scripts() within compiler.compile() above.
|
||||
GDScriptDocGen::generate_docs(this, parser.get_tree());
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
for (const GDScriptWarning &warning : parser.get_warnings()) {
|
||||
if (EngineDebugger::is_active()) {
|
||||
@@ -1266,7 +1112,6 @@ void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_
|
||||
else if (base_cache.is_valid()) {
|
||||
base_cache->get_script_signal_list(r_list);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ class GDScript : public Script {
|
||||
friend class GDScriptFunction;
|
||||
friend class GDScriptAnalyzer;
|
||||
friend class GDScriptCompiler;
|
||||
friend class GDScriptDocGen;
|
||||
friend class GDScriptLanguage;
|
||||
friend struct GDScriptUtilityFunctionsDefinitions;
|
||||
|
||||
@@ -113,16 +114,7 @@ class GDScript : public Script {
|
||||
|
||||
DocData::ClassDoc doc;
|
||||
Vector<DocData::ClassDoc> docs;
|
||||
String doc_brief_description;
|
||||
String doc_description;
|
||||
Vector<DocData::TutorialDoc> doc_tutorials;
|
||||
HashMap<String, String> doc_functions;
|
||||
HashMap<String, String> doc_variables;
|
||||
HashMap<String, String> doc_constants;
|
||||
HashMap<String, String> doc_signals;
|
||||
HashMap<String, DocData::EnumDoc> doc_enums;
|
||||
void _clear_doc();
|
||||
void _update_doc();
|
||||
void _add_doc(const DocData::ClassDoc &p_inner_class);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2152,12 +2152,6 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
|
||||
|
||||
if (p_func) {
|
||||
codegen.generator->set_initial_line(p_func->start_line);
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!p_for_lambda) {
|
||||
p_script->member_lines[func_name] = p_func->start_line;
|
||||
p_script->doc_functions[func_name] = p_func->doc_description;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
codegen.generator->set_initial_line(0);
|
||||
}
|
||||
@@ -2226,23 +2220,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
parsing_classes.insert(p_script);
|
||||
|
||||
p_script->clearing = true;
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->doc_functions.clear();
|
||||
p_script->doc_variables.clear();
|
||||
p_script->doc_constants.clear();
|
||||
p_script->doc_enums.clear();
|
||||
p_script->doc_signals.clear();
|
||||
p_script->doc_tutorials.clear();
|
||||
|
||||
p_script->doc_brief_description = p_class->doc_brief_description;
|
||||
p_script->doc_description = p_class->doc_description;
|
||||
for (int i = 0; i < p_class->doc_tutorials.size(); i++) {
|
||||
DocData::TutorialDoc td;
|
||||
td.title = p_class->doc_tutorials[i].first;
|
||||
td.link = p_class->doc_tutorials[i].second;
|
||||
p_script->doc_tutorials.append(td);
|
||||
}
|
||||
#endif
|
||||
|
||||
p_script->native = Ref<GDScriptNativeClass>();
|
||||
p_script->base = Ref<GDScript>();
|
||||
@@ -2386,9 +2363,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
} else {
|
||||
prop_info.usage = PROPERTY_USAGE_SCRIPT_VARIABLE;
|
||||
}
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->doc_variables[name] = variable->doc_description;
|
||||
#endif
|
||||
|
||||
p_script->member_info[name] = prop_info;
|
||||
p_script->member_indices[name] = minfo;
|
||||
@@ -2401,7 +2375,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
} else {
|
||||
p_script->member_default_values.erase(name);
|
||||
}
|
||||
p_script->member_lines[name] = variable->start_line;
|
||||
#endif
|
||||
} break;
|
||||
|
||||
@@ -2410,12 +2383,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
StringName name = constant->identifier->name;
|
||||
|
||||
p_script->constants.insert(name, constant->initializer->reduced_value);
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->member_lines[name] = constant->start_line;
|
||||
if (!constant->doc_description.is_empty()) {
|
||||
p_script->doc_constants[name] = constant->doc_description;
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case GDScriptParser::ClassNode::Member::ENUM_VALUE: {
|
||||
@@ -2423,18 +2390,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
StringName name = enum_value.identifier->name;
|
||||
|
||||
p_script->constants.insert(name, enum_value.value);
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->member_lines[name] = enum_value.identifier->start_line;
|
||||
if (!p_script->doc_enums.has("@unnamed_enums")) {
|
||||
p_script->doc_enums["@unnamed_enums"] = DocData::EnumDoc();
|
||||
p_script->doc_enums["@unnamed_enums"].name = "@unnamed_enums";
|
||||
}
|
||||
DocData::ConstantDoc const_doc;
|
||||
const_doc.name = enum_value.identifier->name;
|
||||
const_doc.value = Variant(enum_value.value).operator String(); // TODO-DOC: enum value currently is int.
|
||||
const_doc.description = enum_value.doc_description;
|
||||
p_script->doc_enums["@unnamed_enums"].values.push_back(const_doc);
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case GDScriptParser::ClassNode::Member::SIGNAL: {
|
||||
@@ -2447,11 +2402,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
parameters_names.write[j] = signal->parameters[j]->identifier->name;
|
||||
}
|
||||
p_script->_signals[name] = parameters_names;
|
||||
#ifdef TOOLS_ENABLED
|
||||
if (!signal->doc_description.is_empty()) {
|
||||
p_script->doc_signals[name] = signal->doc_description;
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case GDScriptParser::ClassNode::Member::ENUM: {
|
||||
@@ -2459,19 +2409,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
StringName name = enum_n->identifier->name;
|
||||
|
||||
p_script->constants.insert(name, enum_n->dictionary);
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->member_lines[name] = enum_n->start_line;
|
||||
p_script->doc_enums[name] = DocData::EnumDoc();
|
||||
p_script->doc_enums[name].name = name;
|
||||
p_script->doc_enums[name].description = enum_n->doc_description;
|
||||
for (int j = 0; j < enum_n->values.size(); j++) {
|
||||
DocData::ConstantDoc const_doc;
|
||||
const_doc.name = enum_n->values[j].identifier->name;
|
||||
const_doc.value = Variant(enum_n->values[j].value).operator String();
|
||||
const_doc.description = enum_n->values[j].doc_description;
|
||||
p_script->doc_enums[name].values.push_back(const_doc);
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
|
||||
case GDScriptParser::ClassNode::Member::GROUP: {
|
||||
@@ -2519,9 +2456,6 @@ Error GDScriptCompiler::_populate_class_members(GDScript *p_script, const GDScri
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->member_lines[name] = inner_class->start_line;
|
||||
#endif
|
||||
p_script->constants.insert(name, subclass); //once parsed, goes to the list of constants
|
||||
}
|
||||
|
||||
@@ -2614,7 +2548,7 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
|
||||
//well, tough luck, not gonna do anything here
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // TOOLS_ENABLED
|
||||
} else {
|
||||
GDScriptInstance *gi = static_cast<GDScriptInstance *>(si);
|
||||
gi->reload_members();
|
||||
@@ -2623,7 +2557,7 @@ Error GDScriptCompiler::_compile_class(GDScript *p_script, const GDScriptParser:
|
||||
E = N;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif //DEBUG_ENABLED
|
||||
|
||||
for (int i = 0; i < p_class->members.size(); i++) {
|
||||
if (p_class->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
|
||||
@@ -2723,10 +2657,6 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef TOOLS_ENABLED
|
||||
p_script->_update_doc();
|
||||
#endif
|
||||
|
||||
return GDScriptCache::finish_compiling(main_script->get_path());
|
||||
}
|
||||
|
||||
|
||||
@@ -761,7 +761,11 @@ void GDScriptParser::parse_class_member(T *(GDScriptParser::*p_parse_function)()
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Consume doc comments.
|
||||
class_doc_line = MIN(class_doc_line, doc_comment_line - 1);
|
||||
if (has_comment(doc_comment_line)) {
|
||||
|
||||
// Check whether current line has a doc comment
|
||||
if (has_comment(previous.start_line, true)) {
|
||||
member->doc_description = get_doc_comment(previous.start_line, true);
|
||||
} else if (has_comment(doc_comment_line, true)) {
|
||||
if constexpr (std::is_same_v<T, ClassNode>) {
|
||||
get_class_doc_comment(doc_comment_line, member->doc_brief_description, member->doc_description, member->doc_tutorials, true);
|
||||
} else {
|
||||
@@ -3296,8 +3300,15 @@ static bool _in_codeblock(String p_line, bool p_already_in, int *r_block_begins
|
||||
}
|
||||
}
|
||||
|
||||
bool GDScriptParser::has_comment(int p_line) {
|
||||
return tokenizer.get_comments().has(p_line);
|
||||
bool GDScriptParser::has_comment(int p_line, bool p_must_be_doc) {
|
||||
bool has_comment = tokenizer.get_comments().has(p_line);
|
||||
// If there are no comments or if we don't care whether the comment
|
||||
// is a docstring, we have our result.
|
||||
if (!p_must_be_doc || !has_comment) {
|
||||
return has_comment;
|
||||
}
|
||||
|
||||
return tokenizer.get_comments()[p_line].comment.begins_with("##");
|
||||
}
|
||||
|
||||
String GDScriptParser::get_doc_comment(int p_line, bool p_single_line) {
|
||||
|
||||
@@ -1480,7 +1480,7 @@ private:
|
||||
#ifdef TOOLS_ENABLED
|
||||
// Doc comments.
|
||||
int class_doc_line = 0x7FFFFFFF;
|
||||
bool has_comment(int p_line);
|
||||
bool has_comment(int p_line, bool p_must_be_doc = false);
|
||||
String get_doc_comment(int p_line, bool p_single_line = false);
|
||||
void get_class_doc_comment(int p_line, String &p_brief, String &p_desc, Vector<Pair<String, String>> &p_tutorials, bool p_inner_class);
|
||||
#endif // TOOLS_ENABLED
|
||||
|
||||
Reference in New Issue
Block a user