Dump API docs from inline GDScript comments using --doctool --gdscript-docs PATH
This commit is contained in:
parent
26fb911f79
commit
a64137d5dd
|
@ -165,6 +165,7 @@ void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const Met
|
||||||
void DocData::constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc) {
|
void DocData::constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc) {
|
||||||
p_const.name = p_name;
|
p_const.name = p_name;
|
||||||
p_const.value = p_value;
|
p_const.value = p_value;
|
||||||
|
p_const.is_value_valid = (p_value.get_type() != Variant::OBJECT);
|
||||||
p_const.description = p_desc;
|
p_const.description = p_desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1406,7 +1406,7 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
|
||||||
if (!m.return_enum.is_empty()) {
|
if (!m.return_enum.is_empty()) {
|
||||||
enum_text = " enum=\"" + m.return_enum + "\"";
|
enum_text = " enum=\"" + m.return_enum + "\"";
|
||||||
}
|
}
|
||||||
_write_string(f, 3, "<return type=\"" + m.return_type + "\"" + enum_text + " />");
|
_write_string(f, 3, "<return type=\"" + m.return_type.xml_escape(true) + "\"" + enum_text + " />");
|
||||||
}
|
}
|
||||||
if (m.errors_returned.size() > 0) {
|
if (m.errors_returned.size() > 0) {
|
||||||
for (int j = 0; j < m.errors_returned.size(); j++) {
|
for (int j = 0; j < m.errors_returned.size(); j++) {
|
||||||
|
@ -1423,9 +1423,9 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!a.default_value.is_empty()) {
|
if (!a.default_value.is_empty()) {
|
||||||
_write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " default=\"" + a.default_value.xml_escape(true) + "\" />");
|
_write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape(true) + "\"" + enum_text + " default=\"" + a.default_value.xml_escape(true) + "\" />");
|
||||||
} else {
|
} else {
|
||||||
_write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " />");
|
_write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape(true) + "\"" + enum_text + " />");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1440,7 +1440,7 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Error DocTools::save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path) {
|
Error DocTools::save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path, bool p_include_xml_schema) {
|
||||||
for (KeyValue<String, DocData::ClassDoc> &E : class_list) {
|
for (KeyValue<String, DocData::ClassDoc> &E : class_list) {
|
||||||
DocData::ClassDoc &c = E.value;
|
DocData::ClassDoc &c = E.value;
|
||||||
|
|
||||||
|
@ -1452,16 +1452,16 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
|
||||||
}
|
}
|
||||||
|
|
||||||
Error err;
|
Error err;
|
||||||
String save_file = save_path.path_join(c.name + ".xml");
|
String save_file = save_path.path_join(c.name.replace("\"", "").replace("/", "--") + ".xml");
|
||||||
Ref<FileAccess> f = FileAccess::open(save_file, FileAccess::WRITE, &err);
|
Ref<FileAccess> f = FileAccess::open(save_file, FileAccess::WRITE, &err);
|
||||||
|
|
||||||
ERR_CONTINUE_MSG(err != OK, "Can't write doc file: " + save_file + ".");
|
ERR_CONTINUE_MSG(err != OK, "Can't write doc file: " + save_file + ".");
|
||||||
|
|
||||||
_write_string(f, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
|
_write_string(f, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
|
||||||
|
|
||||||
String header = "<class name=\"" + c.name + "\"";
|
String header = "<class name=\"" + c.name.xml_escape(true) + "\"";
|
||||||
if (!c.inherits.is_empty()) {
|
if (!c.inherits.is_empty()) {
|
||||||
header += " inherits=\"" + c.inherits + "\"";
|
header += " inherits=\"" + c.inherits.xml_escape(true) + "\"";
|
||||||
if (c.is_deprecated) {
|
if (c.is_deprecated) {
|
||||||
header += " is_deprecated=\"true\"";
|
header += " is_deprecated=\"true\"";
|
||||||
}
|
}
|
||||||
|
@ -1470,12 +1470,15 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
header += String(" version=\"") + VERSION_BRANCH + "\"";
|
header += String(" version=\"") + VERSION_BRANCH + "\"";
|
||||||
// Reference the XML schema so editors can provide error checking.
|
if (p_include_xml_schema) {
|
||||||
// Modules are nested deep, so change the path to reference the same schema everywhere.
|
// Reference the XML schema so editors can provide error checking.
|
||||||
const String schema_path = save_path.find("modules/") != -1 ? "../../../doc/class.xsd" : "../class.xsd";
|
// Modules are nested deep, so change the path to reference the same schema everywhere.
|
||||||
header += vformat(
|
const String schema_path = save_path.find("modules/") != -1 ? "../../../doc/class.xsd" : "../class.xsd";
|
||||||
R"( xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s">)",
|
header += vformat(
|
||||||
schema_path);
|
R"( xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s")",
|
||||||
|
schema_path);
|
||||||
|
}
|
||||||
|
header += ">";
|
||||||
_write_string(f, 0, header);
|
_write_string(f, 0, header);
|
||||||
|
|
||||||
_write_string(f, 1, "<brief_description>");
|
_write_string(f, 1, "<brief_description>");
|
||||||
|
@ -1521,9 +1524,9 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
|
||||||
const DocData::PropertyDoc &p = c.properties[i];
|
const DocData::PropertyDoc &p = c.properties[i];
|
||||||
|
|
||||||
if (c.properties[i].overridden) {
|
if (c.properties[i].overridden) {
|
||||||
_write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\" overrides=\"" + p.overrides + "\"" + additional_attributes + " />");
|
_write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type.xml_escape(true) + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\" overrides=\"" + p.overrides + "\"" + additional_attributes + " />");
|
||||||
} else {
|
} else {
|
||||||
_write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\"" + additional_attributes + ">");
|
_write_string(f, 2, "<member name=\"" + p.name + "\" type=\"" + p.type.xml_escape(true) + "\" setter=\"" + p.setter + "\" getter=\"" + p.getter + "\"" + additional_attributes + ">");
|
||||||
_write_string(f, 3, _translate_doc_string(p.description).strip_edges().xml_escape());
|
_write_string(f, 3, _translate_doc_string(p.description).strip_edges().xml_escape());
|
||||||
_write_string(f, 2, "</member>");
|
_write_string(f, 2, "</member>");
|
||||||
}
|
}
|
||||||
|
@ -1549,12 +1552,12 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
|
||||||
if (k.is_value_valid) {
|
if (k.is_value_valid) {
|
||||||
if (!k.enumeration.is_empty()) {
|
if (!k.enumeration.is_empty()) {
|
||||||
if (k.is_bitfield) {
|
if (k.is_bitfield) {
|
||||||
_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\"" + additional_attributes + ">");
|
_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value.xml_escape(true) + "\" enum=\"" + k.enumeration + "\" is_bitfield=\"true\"" + additional_attributes + ">");
|
||||||
} else {
|
} else {
|
||||||
_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">");
|
_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value.xml_escape(true) + "\" enum=\"" + k.enumeration + "\"" + additional_attributes + ">");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value + "\"" + additional_attributes + ">");
|
_write_string(f, 2, "<constant name=\"" + k.name + "\" value=\"" + k.value.xml_escape(true) + "\"" + additional_attributes + ">");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!k.enumeration.is_empty()) {
|
if (!k.enumeration.is_empty()) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ public:
|
||||||
bool has_doc(const String &p_class_name);
|
bool has_doc(const String &p_class_name);
|
||||||
void generate(bool p_basic_types = false);
|
void generate(bool p_basic_types = false);
|
||||||
Error load_classes(const String &p_dir);
|
Error load_classes(const String &p_dir);
|
||||||
Error save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path);
|
Error save_classes(const String &p_default_path, const HashMap<String, String> &p_class_path, bool p_include_xml_schema = true);
|
||||||
|
|
||||||
Error _load(Ref<XMLParser> parser);
|
Error _load(Ref<XMLParser> parser);
|
||||||
Error load_compressed(const uint8_t *p_data, int p_compressed_size, int p_uncompressed_size);
|
Error load_compressed(const uint8_t *p_data, int p_compressed_size, int p_uncompressed_size);
|
||||||
|
|
|
@ -113,6 +113,10 @@
|
||||||
#include "modules/mono/editor/bindings_generator.h"
|
#include "modules/mono/editor/bindings_generator.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MODULE_GDSCRIPT_ENABLED
|
||||||
|
#include "modules/gdscript/gdscript.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Static members */
|
/* Static members */
|
||||||
|
|
||||||
// Singletons
|
// Singletons
|
||||||
|
@ -249,6 +253,31 @@ static String get_full_version_string() {
|
||||||
return String(VERSION_FULL_BUILD) + hash;
|
return String(VERSION_FULL_BUILD) + hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(TOOLS_ENABLED) && defined(MODULE_GDSCRIPT_ENABLED)
|
||||||
|
static Vector<String> get_files_with_extension(const String &p_root, const String &p_extension) {
|
||||||
|
Vector<String> paths;
|
||||||
|
|
||||||
|
Ref<DirAccess> dir = DirAccess::open(p_root);
|
||||||
|
if (dir.is_valid()) {
|
||||||
|
dir->list_dir_begin();
|
||||||
|
String fn = dir->get_next();
|
||||||
|
while (!fn.is_empty()) {
|
||||||
|
if (!dir->current_is_hidden() && fn != "." && fn != "..") {
|
||||||
|
if (dir->current_is_dir()) {
|
||||||
|
paths.append_array(get_files_with_extension(p_root.path_join(fn), p_extension));
|
||||||
|
} else if (fn.get_extension() == p_extension) {
|
||||||
|
paths.append(p_root.path_join(fn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn = dir->get_next();
|
||||||
|
}
|
||||||
|
dir->list_dir_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// FIXME: Could maybe be moved to have less code in main.cpp.
|
// FIXME: Could maybe be moved to have less code in main.cpp.
|
||||||
void initialize_physics() {
|
void initialize_physics() {
|
||||||
/// 3D Physics Server
|
/// 3D Physics Server
|
||||||
|
@ -457,6 +486,9 @@ void Main::print_help(const char *p_binary) {
|
||||||
#endif // DISABLE_DEPRECATED
|
#endif // DISABLE_DEPRECATED
|
||||||
OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\n");
|
OS::get_singleton()->print(" --doctool [<path>] Dump the engine API reference to the given <path> (defaults to current dir) in XML format, merging if existing files are found.\n");
|
||||||
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
|
OS::get_singleton()->print(" --no-docbase Disallow dumping the base types (used with --doctool).\n");
|
||||||
|
#ifdef MODULE_GDSCRIPT_ENABLED
|
||||||
|
OS::get_singleton()->print(" --gdscript-docs <path> Rather than dumping the engine API, generate API reference from the inline documentation in the GDScript files found in <path> (used with --doctool).\n");
|
||||||
|
#endif
|
||||||
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
|
OS::get_singleton()->print(" --build-solutions Build the scripting solutions (e.g. for C# projects). Implies --editor and requires a valid project to edit.\n");
|
||||||
OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
|
OS::get_singleton()->print(" --dump-gdextension-interface Generate GDExtension header file 'gdextension_interface.h' in the current folder. This file is the base file required to implement a GDExtension.\n");
|
||||||
OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
|
OS::get_singleton()->print(" --dump-extension-api Generate JSON dump of the Godot API for GDExtension bindings named 'extension_api.json' in the current folder.\n");
|
||||||
|
@ -2470,6 +2502,9 @@ bool Main::start() {
|
||||||
String _export_preset;
|
String _export_preset;
|
||||||
bool export_debug = false;
|
bool export_debug = false;
|
||||||
bool export_pack_only = false;
|
bool export_pack_only = false;
|
||||||
|
#ifdef MODULE_GDSCRIPT_ENABLED
|
||||||
|
String gdscript_docs_path;
|
||||||
|
#endif
|
||||||
#ifndef DISABLE_DEPRECATED
|
#ifndef DISABLE_DEPRECATED
|
||||||
bool converting_project = false;
|
bool converting_project = false;
|
||||||
bool validating_converting_project = false;
|
bool validating_converting_project = false;
|
||||||
|
@ -2530,6 +2565,10 @@ bool Main::start() {
|
||||||
doc_tool_path = ".";
|
doc_tool_path = ".";
|
||||||
parsed_pair = false;
|
parsed_pair = false;
|
||||||
}
|
}
|
||||||
|
#ifdef MODULE_GDSCRIPT_ENABLED
|
||||||
|
} else if (args[i] == "--gdscript-docs") {
|
||||||
|
gdscript_docs_path = args[i + 1];
|
||||||
|
#endif
|
||||||
} else if (args[i] == "--export-release") {
|
} else if (args[i] == "--export-release") {
|
||||||
editor = true; //needs editor
|
editor = true; //needs editor
|
||||||
_export_preset = args[i + 1];
|
_export_preset = args[i + 1];
|
||||||
|
@ -2561,6 +2600,38 @@ bool Main::start() {
|
||||||
uint64_t minimum_time_msec = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/boot_splash/minimum_display_time", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:ms"), 0);
|
uint64_t minimum_time_msec = GLOBAL_DEF(PropertyInfo(Variant::INT, "application/boot_splash/minimum_display_time", PROPERTY_HINT_RANGE, "0,100,1,or_greater,suffix:ms"), 0);
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
|
#ifdef MODULE_GDSCRIPT_ENABLED
|
||||||
|
if (!doc_tool_path.is_empty() && !gdscript_docs_path.is_empty()) {
|
||||||
|
DocTools docs;
|
||||||
|
Error err;
|
||||||
|
|
||||||
|
Vector<String> paths = get_files_with_extension(gdscript_docs_path, "gd");
|
||||||
|
ERR_FAIL_COND_V_MSG(paths.size() == 0, false, "Couldn't find any GDScript files under the given directory: " + gdscript_docs_path);
|
||||||
|
|
||||||
|
for (const String &path : paths) {
|
||||||
|
Ref<GDScript> gdscript = ResourceLoader::load(path);
|
||||||
|
for (const DocData::ClassDoc &class_doc : gdscript->get_documentation()) {
|
||||||
|
docs.add_doc(class_doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doc_tool_path == ".") {
|
||||||
|
doc_tool_path = "./docs";
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<DirAccess> da = DirAccess::create_for_path(doc_tool_path);
|
||||||
|
err = da->make_dir_recursive(doc_tool_path);
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, false, "Error: Can't create GDScript docs directory: " + doc_tool_path + ": " + itos(err));
|
||||||
|
|
||||||
|
HashMap<String, String> doc_data_classes;
|
||||||
|
err = docs.save_classes(doc_tool_path, doc_data_classes, false);
|
||||||
|
ERR_FAIL_COND_V_MSG(err != OK, false, "Error saving GDScript docs:" + itos(err));
|
||||||
|
|
||||||
|
OS::get_singleton()->set_exit_code(EXIT_SUCCESS);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // MODULE_GDSCRIPT_ENABLED
|
||||||
|
|
||||||
if (!doc_tool_path.is_empty()) {
|
if (!doc_tool_path.is_empty()) {
|
||||||
// Needed to instance editor-only classes for their default values
|
// Needed to instance editor-only classes for their default values
|
||||||
Engine::get_singleton()->set_editor_hint(true);
|
Engine::get_singleton()->set_editor_hint(true);
|
||||||
|
|
|
@ -239,6 +239,7 @@ void GDScriptDocGen::generate_docs(GDScript *p_script, const GDP::ClassNode *p_c
|
||||||
DocData::ConstantDoc const_doc;
|
DocData::ConstantDoc const_doc;
|
||||||
const_doc.name = val.identifier->name;
|
const_doc.name = val.identifier->name;
|
||||||
const_doc.value = String(Variant(val.value));
|
const_doc.value = String(Variant(val.value));
|
||||||
|
const_doc.is_value_valid = true;
|
||||||
const_doc.description = val.doc_description;
|
const_doc.description = val.doc_description;
|
||||||
const_doc.enumeration = name;
|
const_doc.enumeration = name;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue