/**************************************************************************/ /* 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" String GDScriptDocGen::_get_script_path(const String &p_path) { return p_path.trim_prefix("res://").quote(); } String GDScriptDocGen::_get_class_name(const GDP::ClassNode &p_class) { const GDP::ClassNode *curr_class = &p_class; if (!curr_class->identifier) { // All inner classes have an 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 an 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; } void GDScriptDocGen::_doctype_from_gdtype(const GDType &p_gdtype, String &r_type, String &r_enum, bool p_is_return) { if (!p_gdtype.is_hard_type()) { r_type = "Variant"; return; } switch (p_gdtype.kind) { case GDType::BUILTIN: if (p_gdtype.builtin_type == Variant::NIL) { r_type = p_is_return ? "void" : "null"; return; } if (p_gdtype.builtin_type == Variant::ARRAY && p_gdtype.has_container_element_type(0)) { _doctype_from_gdtype(p_gdtype.get_container_element_type(0), r_type, r_enum); if (!r_enum.is_empty()) { r_type = "int[]"; r_enum += "[]"; return; } if (!r_type.is_empty() && r_type != "Variant") { r_type += "[]"; return; } } r_type = Variant::get_type_name(p_gdtype.builtin_type); return; case GDType::NATIVE: if (p_gdtype.is_meta_type) { //r_type = GDScriptNativeClass::get_class_static(); r_type = "Object"; // "GDScriptNativeClass" refers to a blank page. return; } r_type = p_gdtype.native_type; return; case GDType::SCRIPT: if (p_gdtype.is_meta_type) { r_type = p_gdtype.script_type.is_valid() ? p_gdtype.script_type->get_class() : Script::get_class_static(); return; } if (p_gdtype.script_type.is_valid()) { if (p_gdtype.script_type->get_global_name() != StringName()) { r_type = p_gdtype.script_type->get_global_name(); return; } if (!p_gdtype.script_type->get_path().is_empty()) { r_type = _get_script_path(p_gdtype.script_type->get_path()); return; } } if (!p_gdtype.script_path.is_empty()) { r_type = _get_script_path(p_gdtype.script_path); return; } r_type = "Object"; return; case GDType::CLASS: if (p_gdtype.is_meta_type) { r_type = GDScript::get_class_static(); return; } r_type = _get_class_name(*p_gdtype.class_type); return; case GDType::ENUM: if (p_gdtype.is_meta_type) { r_type = "Dictionary"; return; } r_type = "int"; r_enum = String(p_gdtype.native_type).replace("::", "."); if (r_enum.begins_with("res://")) { r_enum = r_enum.trim_prefix("res://"); int dot_pos = r_enum.rfind("."); if (dot_pos >= 0) { r_enum = r_enum.left(dot_pos).quote() + r_enum.substr(dot_pos); } } return; case GDType::VARIANT: case GDType::RESOLVING: case GDType::UNRESOLVED: r_type = "Variant"; return; } } String GDScriptDocGen::_docvalue_from_variant(const Variant &p_variant, int p_recursion_level) { constexpr int MAX_RECURSION_LEVEL = 2; switch (p_variant.get_type()) { case Variant::STRING: return String(p_variant).c_escape().quote(); case Variant::OBJECT: return ""; case Variant::DICTIONARY: { const Dictionary dict = p_variant; if (dict.is_empty()) { return "{}"; } if (p_recursion_level > MAX_RECURSION_LEVEL) { return "{...}"; } List keys; dict.get_key_list(&keys); keys.sort(); String data; for (List::Element *E = keys.front(); E; E = E->next()) { if (E->prev()) { data += ", "; } data += _docvalue_from_variant(E->get(), p_recursion_level + 1) + ": " + _docvalue_from_variant(dict[E->get()], p_recursion_level + 1); } return "{" + data + "}"; } break; case Variant::ARRAY: { const Array array = p_variant; String result; if (array.get_typed_builtin() != Variant::NIL) { result += "Array["; Ref