2014-02-10 02:10:30 +01:00
/**************************************************************************/
2020-11-29 04:42:06 +01:00
/* doc_tools.cpp */
2014-02-10 02:10:30 +01:00
/**************************************************************************/
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2020-11-29 04:42:06 +01:00
# include "doc_tools.h"
2014-02-16 01:16:33 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/engine.h"
# include "core/config/project_settings.h"
# include "core/core_constants.h"
2018-09-11 18:13:45 +02:00
# include "core/io/compression.h"
2021-06-11 14:51:48 +02:00
# include "core/io/dir_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/marshalls.h"
2021-05-13 22:34:34 +02:00
# include "core/io/resource_importer.h"
2020-11-07 23:33:38 +01:00
# include "core/object/script_language.h"
2021-12-15 16:01:06 +01:00
# include "core/string/translation.h"
2021-05-07 22:21:03 +02:00
# include "editor/editor_settings.h"
2023-03-09 09:41:52 +01:00
# include "editor/export/editor_export.h"
2014-06-30 03:41:02 +02:00
# include "scene/resources/theme.h"
2022-08-08 18:29:36 +02:00
# include "scene/theme/theme_db.h"
2014-02-16 01:16:33 +01:00
2020-04-20 17:57:38 +02:00
// Used for a hack preserving Mono properties on non-Mono builds.
2021-11-12 13:42:58 +01:00
# include "modules/modules_enabled.gen.h" // For mono.
2020-04-20 17:57:38 +02:00
2021-12-15 16:01:06 +01:00
static String _get_indent ( const String & p_text ) {
String indent ;
bool has_text = false ;
int line_start = 0 ;
for ( int i = 0 ; i < p_text . length ( ) ; i + + ) {
const char32_t c = p_text [ i ] ;
if ( c = = ' \n ' ) {
line_start = i + 1 ;
} else if ( c > 32 ) {
has_text = true ;
indent = p_text . substr ( line_start , i - line_start ) ;
break ; // Indentation of the first line that has text.
}
}
if ( ! has_text ) {
return p_text ;
}
return indent ;
}
static String _translate_doc_string ( const String & p_text ) {
const String indent = _get_indent ( p_text ) ;
const String message = p_text . dedent ( ) . strip_edges ( ) ;
const String translated = TranslationServer : : get_singleton ( ) - > doc_translate ( message , " " ) ;
// No need to restore stripped edges because they'll be stripped again later.
return translated . indent ( indent ) ;
}
2024-02-18 20:41:01 +01:00
// Comparator for constructors, based on `MetodDoc` operator.
struct ConstructorCompare {
_FORCE_INLINE_ bool operator ( ) ( const DocData : : MethodDoc & p_lhs , const DocData : : MethodDoc & p_rhs ) const {
// Must be a constructor (i.e. assume named for the class)
// We want this arbitrary order for a class "Foo":
// - 1. Default constructor: Foo()
// - 2. Copy constructor: Foo(Foo)
// - 3+. Other constructors Foo(Bar, ...) based on first argument's name
if ( p_lhs . arguments . is_empty ( ) | | p_rhs . arguments . is_empty ( ) ) { // 1.
return p_lhs . arguments . size ( ) < p_rhs . arguments . size ( ) ;
}
if ( p_lhs . arguments [ 0 ] . type = = p_lhs . return_type | | p_rhs . arguments [ 0 ] . type = = p_lhs . return_type ) { // 2.
return ( p_lhs . arguments [ 0 ] . type = = p_lhs . return_type ) | | ( p_rhs . arguments [ 0 ] . type ! = p_lhs . return_type ) ;
}
return p_lhs . arguments [ 0 ] < p_rhs . arguments [ 0 ] ;
}
} ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
// Comparator for operators, compares on name and type.
struct OperatorCompare {
_FORCE_INLINE_ bool operator ( ) ( const DocData : : MethodDoc & p_lhs , const DocData : : MethodDoc & p_rhs ) const {
if ( p_lhs . name = = p_rhs . name ) {
if ( p_lhs . arguments . size ( ) = = p_rhs . arguments . size ( ) ) {
if ( p_lhs . arguments . is_empty ( ) ) {
return false ;
}
return p_lhs . arguments [ 0 ] . type < p_rhs . arguments [ 0 ] . type ;
}
return p_lhs . arguments . size ( ) < p_rhs . arguments . size ( ) ;
2020-05-14 16:41:43 +02:00
}
2024-02-18 20:41:01 +01:00
return p_lhs . name . naturalcasecmp_to ( p_rhs . name ) < 0 ;
}
} ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
// Comparator for methods, compares on names.
struct MethodCompare {
_FORCE_INLINE_ bool operator ( ) ( const DocData : : MethodDoc & p_lhs , const DocData : : MethodDoc & p_rhs ) const {
return p_lhs . name . naturalcasecmp_to ( p_rhs . name ) < 0 ;
}
} ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
static void merge_constructors ( Vector < DocData : : MethodDoc > & p_to , const Vector < DocData : : MethodDoc > & p_from ) {
// Get data from `p_from`, to avoid mutation checks.
const DocData : : MethodDoc * from_ptr = p_from . ptr ( ) ;
int64_t from_size = p_from . size ( ) ;
2022-08-28 08:17:25 +02:00
2024-02-18 20:41:01 +01:00
// TODO: Improve constructor merging.
for ( DocData : : MethodDoc & to : p_to ) {
for ( int64_t from_i = 0 ; from_i < from_size ; + + from_i ) {
const DocData : : MethodDoc & from = from_ptr [ from_i ] ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
// Compare argument count first.
if ( from . arguments . size ( ) ! = to . arguments . size ( ) ) {
continue ;
}
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
if ( from . name ! = to . name ) {
continue ;
}
2021-08-22 14:12:04 +02:00
2024-02-18 20:41:01 +01:00
{
// Since constructors can repeat, we need to check the type of
// the arguments so we make sure they are different.
int64_t arg_count = from . arguments . size ( ) ;
Vector < bool > arg_used ;
arg_used . resize_zeroed ( arg_count ) ;
// Also there is no guarantee that argument ordering will match,
// so we have to check one by one so we make sure we have an exact match.
for ( int64_t arg_i = 0 ; arg_i < arg_count ; + + arg_i ) {
for ( int64_t arg_j = 0 ; arg_j < arg_count ; + + arg_j ) {
if ( from . arguments [ arg_i ] . type = = to . arguments [ arg_j ] . type & & ! arg_used [ arg_j ] ) {
arg_used . write [ arg_j ] = true ;
break ;
2021-08-22 14:12:04 +02:00
}
}
2024-02-18 20:41:01 +01:00
}
bool not_the_same = false ;
for ( int64_t arg_i = 0 ; arg_i < arg_count ; + + arg_i ) {
if ( ! arg_used [ arg_i ] ) { // At least one of the arguments was different.
not_the_same = true ;
break ;
2020-05-14 16:41:43 +02:00
}
}
2024-02-18 20:41:01 +01:00
if ( not_the_same ) {
continue ;
}
2021-09-21 04:49:02 +02:00
}
2024-02-18 20:41:01 +01:00
to . description = from . description ;
to . is_deprecated = from . is_deprecated ;
to . deprecated_message = from . deprecated_message ;
to . is_experimental = from . is_experimental ;
to . experimental_message = from . experimental_message ;
break ;
2021-09-21 04:49:02 +02:00
}
2024-02-18 20:41:01 +01:00
}
}
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
static void merge_methods ( Vector < DocData : : MethodDoc > & p_to , const Vector < DocData : : MethodDoc > & p_from ) {
// Get data from `p_to`, to avoid mutation checks. Searching will be done in the sorted `p_to` from the (potentially) unsorted `p_from`.
DocData : : MethodDoc * to_ptrw = p_to . ptrw ( ) ;
int64_t to_size = p_to . size ( ) ;
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
SearchArray < DocData : : MethodDoc , MethodCompare > search_array ;
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
for ( const DocData : : MethodDoc & from : p_from ) {
int64_t found = search_array . bisect ( to_ptrw , to_size , from , true ) ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
if ( found > = to_size ) {
continue ;
2014-02-10 02:10:30 +01:00
}
2024-02-18 20:41:01 +01:00
DocData : : MethodDoc & to = to_ptrw [ found ] ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
// Check found entry on name.
if ( to . name = = from . name ) {
to . description = from . description ;
to . is_deprecated = from . is_deprecated ;
to . deprecated_message = from . deprecated_message ;
to . is_experimental = from . is_experimental ;
to . experimental_message = from . experimental_message ;
to . keywords = from . keywords ;
2014-02-10 02:10:30 +01:00
}
2024-02-18 20:41:01 +01:00
}
}
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
static void merge_constants ( Vector < DocData : : ConstantDoc > & p_to , const Vector < DocData : : ConstantDoc > & p_from ) {
// Get data from `p_from`, to avoid mutation checks. Searching will be done in the sorted `p_from` from the unsorted `p_to`.
const DocData : : ConstantDoc * from_ptr = p_from . ptr ( ) ;
int64_t from_size = p_from . size ( ) ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
SearchArray < DocData : : ConstantDoc > search_array ;
for ( DocData : : ConstantDoc & to : p_to ) {
int64_t found = search_array . bisect ( from_ptr , from_size , to , true ) ;
if ( found > = from_size ) {
continue ;
2014-02-10 02:10:30 +01:00
}
2024-02-18 20:41:01 +01:00
// Check found entry on name.
const DocData : : ConstantDoc & from = from_ptr [ found ] ;
2022-07-04 17:56:34 +02:00
2024-02-18 20:41:01 +01:00
if ( from . name = = to . name ) {
to . description = from . description ;
to . is_deprecated = from . is_deprecated ;
to . deprecated_message = from . deprecated_message ;
to . is_experimental = from . is_experimental ;
to . experimental_message = from . experimental_message ;
to . keywords = from . keywords ;
2022-07-04 17:56:34 +02:00
}
2024-02-18 20:41:01 +01:00
}
}
2022-07-04 17:56:34 +02:00
2024-02-18 20:41:01 +01:00
static void merge_properties ( Vector < DocData : : PropertyDoc > & p_to , const Vector < DocData : : PropertyDoc > & p_from ) {
// Get data from `p_to`, to avoid mutation checks. Searching will be done in the sorted `p_to` from the (potentially) unsorted `p_from`.
DocData : : PropertyDoc * to_ptrw = p_to . ptrw ( ) ;
int64_t to_size = p_to . size ( ) ;
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
SearchArray < DocData : : PropertyDoc > search_array ;
for ( const DocData : : PropertyDoc & from : p_from ) {
int64_t found = search_array . bisect ( to_ptrw , to_size , from , true ) ;
if ( found > = to_size ) {
continue ;
2014-02-10 02:10:30 +01:00
}
2024-02-18 20:41:01 +01:00
DocData : : PropertyDoc & to = to_ptrw [ found ] ;
2014-06-30 03:41:02 +02:00
2024-02-18 20:41:01 +01:00
// Check found entry on name.
if ( to . name = = from . name ) {
to . description = from . description ;
to . is_deprecated = from . is_deprecated ;
to . deprecated_message = from . deprecated_message ;
to . is_experimental = from . is_experimental ;
to . experimental_message = from . experimental_message ;
to . keywords = from . keywords ;
}
}
}
2014-06-30 03:41:02 +02:00
2024-02-18 20:41:01 +01:00
static void merge_theme_properties ( Vector < DocData : : ThemeItemDoc > & p_to , const Vector < DocData : : ThemeItemDoc > & p_from ) {
// Get data from `p_to`, to avoid mutation checks. Searching will be done in the sorted `p_to` from the (potentially) unsorted `p_from`.
DocData : : ThemeItemDoc * to_ptrw = p_to . ptrw ( ) ;
int64_t to_size = p_to . size ( ) ;
SearchArray < DocData : : ThemeItemDoc > search_array ;
for ( const DocData : : ThemeItemDoc & from : p_from ) {
int64_t found = search_array . bisect ( to_ptrw , to_size , from , true ) ;
if ( found > = to_size ) {
continue ;
2014-06-30 03:41:02 +02:00
}
2020-04-20 17:57:38 +02:00
2024-02-18 20:41:01 +01:00
DocData : : ThemeItemDoc & to = to_ptrw [ found ] ;
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
// Check found entry on name and data type.
if ( to . name = = from . name & & to . data_type = = from . data_type ) {
to . description = from . description ;
to . keywords = from . keywords ;
}
}
}
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
static void merge_operators ( Vector < DocData : : MethodDoc > & p_to , const Vector < DocData : : MethodDoc > & p_from ) {
// Get data from `p_to`, to avoid mutation checks. Searching will be done in the sorted `p_to` from the (potentially) unsorted `p_from`.
DocData : : MethodDoc * to_ptrw = p_to . ptrw ( ) ;
int64_t to_size = p_to . size ( ) ;
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
SearchArray < DocData : : MethodDoc , OperatorCompare > search_array ;
2021-09-21 04:49:02 +02:00
2024-02-18 20:41:01 +01:00
for ( const DocData : : MethodDoc & from : p_from ) {
int64_t found = search_array . bisect ( to_ptrw , to_size , from , true ) ;
if ( found > = to_size ) {
continue ;
}
DocData : : MethodDoc & to = to_ptrw [ found ] ;
// Check found entry on name and argument.
if ( to . name = = from . name & & to . arguments . size ( ) = = from . arguments . size ( ) & & ( to . arguments . is_empty ( ) | | to . arguments [ 0 ] . type = = from . arguments [ 0 ] . type ) ) {
to . description = from . description ;
to . is_deprecated = from . is_deprecated ;
to . deprecated_message = from . deprecated_message ;
to . is_experimental = from . is_experimental ;
to . experimental_message = from . experimental_message ;
}
}
}
void DocTools : : merge_from ( const DocTools & p_data ) {
for ( KeyValue < String , DocData : : ClassDoc > & E : class_list ) {
DocData : : ClassDoc & c = E . value ;
if ( ! p_data . class_list . has ( c . name ) ) {
continue ;
2021-09-21 04:49:02 +02:00
}
2024-02-18 20:41:01 +01:00
const DocData : : ClassDoc & cf = p_data . class_list [ c . name ] ;
c . is_deprecated = cf . is_deprecated ;
c . deprecated_message = cf . deprecated_message ;
c . is_experimental = cf . is_experimental ;
c . experimental_message = cf . experimental_message ;
c . keywords = cf . keywords ;
c . description = cf . description ;
c . brief_description = cf . brief_description ;
c . tutorials = cf . tutorials ;
merge_constructors ( c . constructors , cf . constructors ) ;
merge_methods ( c . methods , cf . methods ) ;
merge_methods ( c . signals , cf . signals ) ;
merge_constants ( c . constants , cf . constants ) ;
merge_methods ( c . annotations , cf . annotations ) ;
merge_properties ( c . properties , cf . properties ) ;
merge_theme_properties ( c . theme_properties , cf . theme_properties ) ;
merge_operators ( c . operators , cf . operators ) ;
2020-04-20 17:57:38 +02:00
# ifndef MODULE_MONO_ENABLED
// The Mono module defines some properties that we want to keep when
// re-generating docs with a non-Mono build, to prevent pointless diffs
// (and loss of descriptions) depending on the config of the doc writer.
// We use a horrible hack to force keeping the relevant properties,
// hardcoded below. At least it's an ad hoc hack... ¯\_(ツ)_/¯
// Don't show this to your kids.
if ( c . name = = " @GlobalScope " ) {
// Retrieve GodotSharp singleton.
for ( int j = 0 ; j < cf . properties . size ( ) ; j + + ) {
if ( cf . properties [ j ] . name = = " GodotSharp " ) {
c . properties . push_back ( cf . properties [ j ] ) ;
2024-02-18 20:41:01 +01:00
c . properties . sort ( ) ;
break ;
2020-04-20 17:57:38 +02:00
}
}
}
# endif
2014-02-10 02:10:30 +01:00
}
}
2020-11-29 04:42:06 +01:00
void DocTools : : add_doc ( const DocData : : ClassDoc & p_class_doc ) {
2021-12-09 10:42:46 +01:00
ERR_FAIL_COND ( p_class_doc . name . is_empty ( ) ) ;
2020-11-29 03:37:57 +01:00
class_list [ p_class_doc . name ] = p_class_doc ;
2023-08-16 14:00:59 +02:00
inheriting [ p_class_doc . inherits ] . insert ( p_class_doc . name ) ;
2020-11-29 03:37:57 +01:00
}
2020-11-29 04:42:06 +01:00
void DocTools : : remove_doc ( const String & p_class_name ) {
2021-12-09 10:42:46 +01:00
ERR_FAIL_COND ( p_class_name . is_empty ( ) | | ! class_list . has ( p_class_name ) ) ;
2023-08-16 14:00:59 +02:00
const String & inherits = class_list [ p_class_name ] . inherits ;
if ( inheriting . has ( inherits ) ) {
inheriting [ inherits ] . erase ( p_class_name ) ;
if ( inheriting [ inherits ] . is_empty ( ) ) {
inheriting . erase ( inherits ) ;
}
}
2020-11-29 03:37:57 +01:00
class_list . erase ( p_class_name ) ;
}
2020-11-29 04:42:06 +01:00
bool DocTools : : has_doc ( const String & p_class_name ) {
2021-12-09 10:42:46 +01:00
if ( p_class_name . is_empty ( ) ) {
2020-11-29 03:37:57 +01:00
return false ;
}
return class_list . has ( p_class_name ) ;
}
2019-09-03 12:42:34 +02:00
static Variant get_documentation_default_value ( const StringName & p_class_name , const StringName & p_property_name , bool & r_default_value_valid ) {
Variant default_value = Variant ( ) ;
r_default_value_valid = false ;
2022-11-09 08:59:49 +01:00
if ( ClassDB : : can_instantiate ( p_class_name ) & & ! ClassDB : : is_virtual ( p_class_name ) ) { // Keep this condition in sync with ClassDB::class_get_default_property_value.
2019-09-03 12:42:34 +02:00
default_value = ClassDB : : class_get_default_property_value ( p_class_name , p_property_name , & r_default_value_valid ) ;
} else {
2021-06-18 00:03:09 +02:00
// Cannot get default value of classes that can't be instantiated
2019-09-03 12:42:34 +02:00
List < StringName > inheriting_classes ;
ClassDB : : get_direct_inheriters_from_class ( p_class_name , & inheriting_classes ) ;
for ( List < StringName > : : Element * E2 = inheriting_classes . front ( ) ; E2 ; E2 = E2 - > next ( ) ) {
2021-06-18 00:03:09 +02:00
if ( ClassDB : : can_instantiate ( E2 - > get ( ) ) ) {
2019-09-03 12:42:34 +02:00
default_value = ClassDB : : class_get_default_property_value ( E2 - > get ( ) , p_property_name , & r_default_value_valid ) ;
2020-05-14 16:41:43 +02:00
if ( r_default_value_valid ) {
2019-09-03 12:42:34 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2019-09-03 12:42:34 +02:00
}
}
}
return default_value ;
}
2023-10-31 18:27:17 +01:00
void DocTools : : generate ( BitField < GenerateFlags > p_flags ) {
// This may involve instantiating classes that are only usable from the main thread
// (which is in fact the case of the core API).
ERR_FAIL_COND ( ! Thread : : is_main_thread ( ) ) ;
2022-08-08 14:18:26 +02:00
// Add ClassDB-exposed classes.
{
List < StringName > classes ;
2023-10-31 18:27:17 +01:00
if ( p_flags . has_flag ( GENERATE_FLAG_EXTENSION_CLASSES_ONLY ) ) {
ClassDB : : get_extensions_class_list ( & classes ) ;
} else {
ClassDB : : get_class_list ( & classes ) ;
// Move ProjectSettings, so that other classes can register properties there.
classes . move_to_back ( classes . find ( " ProjectSettings " ) ) ;
}
2022-08-08 14:18:26 +02:00
bool skip_setter_getter_methods = true ;
// Populate documentation data for each exposed class.
while ( classes . size ( ) ) {
2023-10-31 18:27:17 +01:00
const String & name = classes . front ( ) - > get ( ) ;
2022-08-08 14:18:26 +02:00
if ( ! ClassDB : : is_class_exposed ( name ) ) {
print_verbose ( vformat ( " Class '%s' is not exposed, skipping. " , name ) ) ;
classes . pop_front ( ) ;
continue ;
}
2017-01-04 05:16:14 +01:00
2023-11-18 23:40:56 +01:00
const String & cname = name ;
2022-08-08 14:18:26 +02:00
// Property setters and getters do not get exposed as individual methods.
HashSet < StringName > setters_getters ;
2018-06-11 18:41:16 +02:00
2022-08-08 14:18:26 +02:00
class_list [ cname ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ cname ] ;
c . name = cname ;
c . inherits = ClassDB : : get_parent_class ( name ) ;
2022-01-18 22:07:47 +01:00
2023-08-16 14:00:59 +02:00
inheriting [ c . inherits ] . insert ( cname ) ;
2022-08-08 14:18:26 +02:00
List < PropertyInfo > properties ;
List < PropertyInfo > own_properties ;
2019-09-03 12:42:34 +02:00
2021-05-13 22:34:34 +02:00
// Special cases for editor/project settings, and ResourceImporter classes,
// we have to rely on Object's property list to get settings and import options.
// Otherwise we just use ClassDB's property list (pure registered properties).
bool properties_from_instance = true ; // To skip `script`, etc.
bool import_option = false ; // Special case for default value.
HashMap < StringName , Variant > import_options_default ;
2022-08-08 14:18:26 +02:00
if ( name = = " EditorSettings " ) {
// We don't create the full blown EditorSettings (+ config file) with `create()`,
// instead we just make a local instance to get default values.
Ref < EditorSettings > edset = memnew ( EditorSettings ) ;
edset - > get_property_list ( & properties ) ;
own_properties = properties ;
} else if ( name = = " ProjectSettings " ) {
ProjectSettings : : get_singleton ( ) - > get_property_list ( & properties ) ;
own_properties = properties ;
2021-05-13 22:34:34 +02:00
} else if ( ClassDB : : is_parent_class ( name , " ResourceImporter " ) & & name ! = " EditorImportPlugin " & & ClassDB : : can_instantiate ( name ) ) {
import_option = true ;
ResourceImporter * resimp = Object : : cast_to < ResourceImporter > ( ClassDB : : instantiate ( name ) ) ;
List < ResourceImporter : : ImportOption > options ;
resimp - > get_import_options ( " " , & options ) ;
for ( int i = 0 ; i < options . size ( ) ; i + + ) {
const PropertyInfo & prop = options [ i ] . option ;
properties . push_back ( prop ) ;
import_options_default [ prop . name ] = options [ i ] . default_value ;
}
own_properties = properties ;
memdelete ( resimp ) ;
2023-03-09 09:41:52 +01:00
} else if ( name . begins_with ( " EditorExportPlatform " ) & & ClassDB : : can_instantiate ( name ) ) {
2021-05-13 22:34:34 +02:00
properties_from_instance = false ;
2023-03-09 09:41:52 +01:00
Ref < EditorExportPlatform > platform = Object : : cast_to < EditorExportPlatform > ( ClassDB : : instantiate ( name ) ) ;
if ( platform . is_valid ( ) ) {
List < EditorExportPlatform : : ExportOption > options ;
platform - > get_export_options ( & options ) ;
for ( const EditorExportPlatform : : ExportOption & E : options ) {
properties . push_back ( E . option ) ;
}
own_properties = properties ;
}
2022-08-08 14:18:26 +02:00
} else {
2021-05-13 22:34:34 +02:00
properties_from_instance = false ;
2022-08-08 14:18:26 +02:00
ClassDB : : get_property_list ( name , & properties ) ;
ClassDB : : get_property_list ( name , & own_properties , true ) ;
2020-05-14 16:41:43 +02:00
}
2017-01-04 05:16:14 +01:00
2023-05-04 15:18:56 +02:00
// Sort is still needed here to handle inherited properties, even though it is done below, do not remove.
2022-08-08 14:18:26 +02:00
properties . sort ( ) ;
own_properties . sort ( ) ;
2019-09-03 12:42:34 +02:00
2022-08-08 14:18:26 +02:00
List < PropertyInfo > : : Element * EO = own_properties . front ( ) ;
for ( const PropertyInfo & E : properties ) {
bool inherited = true ;
if ( EO & & EO - > get ( ) = = E ) {
inherited = false ;
EO = EO - > next ( ) ;
2021-12-02 20:38:49 +01:00
}
2019-06-29 14:51:33 +02:00
2021-05-13 22:34:34 +02:00
if ( properties_from_instance ) {
2024-02-08 19:26:55 +01:00
if ( E . name = = " resource_local_to_scene " | | E . name = = " resource_name " | | E . name = = " resource_path " | | E . name = = " script " | | E . name = = " resource_scene_unique_id " ) {
2021-05-13 22:34:34 +02:00
// Don't include spurious properties from Object property list.
continue ;
}
}
2022-08-08 14:18:26 +02:00
if ( E . usage & PROPERTY_USAGE_GROUP | | E . usage & PROPERTY_USAGE_SUBGROUP | | E . usage & PROPERTY_USAGE_CATEGORY | | E . usage & PROPERTY_USAGE_INTERNAL | | ( E . type = = Variant : : NIL & & E . usage & PROPERTY_USAGE_ARRAY ) ) {
2021-05-07 22:21:03 +02:00
continue ;
}
2022-08-08 14:18:26 +02:00
DocData : : PropertyDoc prop ;
prop . name = E . name ;
prop . overridden = inherited ;
if ( inherited ) {
String parent = ClassDB : : get_parent_class ( c . name ) ;
while ( ! ClassDB : : has_property ( parent , prop . name , true ) ) {
parent = ClassDB : : get_parent_class ( parent ) ;
}
prop . overrides = parent ;
2020-01-22 14:22:21 +01:00
}
2022-08-08 14:18:26 +02:00
bool default_value_valid = false ;
Variant default_value ;
if ( name = = " ProjectSettings " ) {
// Special case for project settings, so that settings are not taken from the current project's settings
2021-05-13 22:34:34 +02:00
if ( ! ProjectSettings : : get_singleton ( ) - > is_builtin_setting ( E . name ) ) {
2020-01-22 14:22:21 +01:00
continue ;
2020-05-14 16:41:43 +02:00
}
2022-08-08 14:18:26 +02:00
if ( E . usage & PROPERTY_USAGE_EDITOR ) {
if ( ! ProjectSettings : : get_singleton ( ) - > get_ignore_value_in_docs ( E . name ) ) {
default_value = ProjectSettings : : get_singleton ( ) - > property_get_revert ( E . name ) ;
default_value_valid = true ;
}
}
2023-07-31 14:34:09 +02:00
} else if ( name = = " EditorSettings " ) {
// Special case for editor settings, to prevent hardware or OS specific settings to affect the result.
2021-05-13 22:34:34 +02:00
} else if ( import_option ) {
default_value = import_options_default [ E . name ] ;
default_value_valid = true ;
2022-08-08 14:18:26 +02:00
} else {
default_value = get_documentation_default_value ( name , E . name , default_value_valid ) ;
if ( inherited ) {
bool base_default_value_valid = false ;
Variant base_default_value = get_documentation_default_value ( ClassDB : : get_parent_class ( name ) , E . name , base_default_value_valid ) ;
if ( ! default_value_valid | | ! base_default_value_valid | | default_value = = base_default_value ) {
continue ;
}
}
2020-01-22 14:22:21 +01:00
}
2019-06-01 15:42:22 +02:00
2022-08-08 14:18:26 +02:00
if ( default_value_valid & & default_value . get_type ( ) ! = Variant : : OBJECT ) {
2022-11-27 08:56:53 +01:00
prop . default_value = DocData : : get_default_value_string ( default_value ) ;
2022-08-08 14:18:26 +02:00
}
StringName setter = ClassDB : : get_property_setter ( name , E . name ) ;
StringName getter = ClassDB : : get_property_getter ( name , E . name ) ;
prop . setter = setter ;
prop . getter = getter ;
bool found_type = false ;
if ( getter ! = StringName ( ) ) {
MethodBind * mb = ClassDB : : get_method ( name , getter ) ;
if ( mb ) {
PropertyInfo retinfo = mb - > get_return_info ( ) ;
found_type = true ;
if ( retinfo . type = = Variant : : INT & & retinfo . usage & ( PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD ) ) {
prop . enumeration = retinfo . class_name ;
2023-06-15 16:06:22 +02:00
prop . is_bitfield = retinfo . usage & PROPERTY_USAGE_CLASS_IS_BITFIELD ;
2022-08-08 14:18:26 +02:00
prop . type = " int " ;
} else if ( retinfo . class_name ! = StringName ( ) ) {
prop . type = retinfo . class_name ;
} else if ( retinfo . type = = Variant : : ARRAY & & retinfo . hint = = PROPERTY_HINT_ARRAY_TYPE ) {
prop . type = retinfo . hint_string + " [] " ;
} else if ( retinfo . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
prop . type = retinfo . hint_string ;
} else if ( retinfo . type = = Variant : : NIL & & retinfo . usage & PROPERTY_USAGE_NIL_IS_VARIANT ) {
prop . type = " Variant " ;
} else if ( retinfo . type = = Variant : : NIL ) {
prop . type = " void " ;
} else {
prop . type = Variant : : get_type_name ( retinfo . type ) ;
}
}
2019-06-29 14:51:33 +02:00
2022-08-08 14:18:26 +02:00
setters_getters . insert ( getter ) ;
}
if ( setter ! = StringName ( ) ) {
setters_getters . insert ( setter ) ;
}
if ( ! found_type ) {
if ( E . type = = Variant : : OBJECT & & E . hint = = PROPERTY_HINT_RESOURCE_TYPE ) {
prop . type = E . hint_string ;
2017-08-24 00:10:32 +02:00
} else {
2022-08-08 14:18:26 +02:00
prop . type = Variant : : get_type_name ( E . type ) ;
2017-08-24 00:10:32 +02:00
}
}
2017-11-24 16:32:59 +01:00
2022-08-08 14:18:26 +02:00
c . properties . push_back ( prop ) ;
2017-11-24 16:32:59 +01:00
}
2023-05-04 15:18:56 +02:00
c . properties . sort ( ) ;
2022-08-08 14:18:26 +02:00
List < MethodInfo > method_list ;
ClassDB : : get_method_list ( name , & method_list , true ) ;
2017-08-24 00:10:32 +02:00
2022-08-08 14:18:26 +02:00
for ( const MethodInfo & E : method_list ) {
if ( E . name . is_empty ( ) | | ( E . name [ 0 ] = = ' _ ' & & ! ( E . flags & METHOD_FLAG_VIRTUAL ) ) ) {
continue ; //hidden, don't count
2020-05-14 16:41:43 +02:00
}
2017-01-04 05:16:14 +01:00
2022-08-08 14:18:26 +02:00
if ( skip_setter_getter_methods & & setters_getters . has ( E . name ) ) {
// Don't skip parametric setters and getters, i.e. method which require
// one or more parameters to define what property should be set or retrieved.
// E.g. CPUParticles3D::set_param(Parameter param, float value).
if ( E . arguments . size ( ) = = 0 /* getter */ | | ( E . arguments . size ( ) = = 1 & & E . return_val . type = = Variant : : NIL /* setter */ ) ) {
continue ;
}
2019-06-27 16:10:09 +02:00
}
2017-11-24 16:32:59 +01:00
2022-08-08 14:18:26 +02:00
DocData : : MethodDoc method ;
DocData : : method_doc_from_methodinfo ( method , E , " " ) ;
2014-02-10 02:10:30 +01:00
2022-08-08 14:18:26 +02:00
Vector < Error > errs = ClassDB : : get_method_error_return_values ( name , E . name ) ;
if ( errs . size ( ) ) {
if ( ! errs . has ( OK ) ) {
errs . insert ( 0 , OK ) ;
}
for ( int i = 0 ; i < errs . size ( ) ; i + + ) {
if ( ! method . errors_returned . has ( errs [ i ] ) ) {
method . errors_returned . push_back ( errs [ i ] ) ;
}
2021-08-24 20:16:25 +02:00
}
}
2022-08-08 14:18:26 +02:00
c . methods . push_back ( method ) ;
2021-08-24 20:16:25 +02:00
}
2024-02-18 20:41:01 +01:00
c . methods . sort_custom < MethodCompare > ( ) ;
2023-05-04 15:18:56 +02:00
2022-08-08 14:18:26 +02:00
List < MethodInfo > signal_list ;
ClassDB : : get_signal_list ( name , & signal_list , true ) ;
2014-02-10 02:10:30 +01:00
2022-08-08 14:18:26 +02:00
if ( signal_list . size ( ) ) {
for ( List < MethodInfo > : : Element * EV = signal_list . front ( ) ; EV ; EV = EV - > next ( ) ) {
DocData : : MethodDoc signal ;
signal . name = EV - > get ( ) . name ;
for ( int i = 0 ; i < EV - > get ( ) . arguments . size ( ) ; i + + ) {
const PropertyInfo & arginfo = EV - > get ( ) . arguments [ i ] ;
DocData : : ArgumentDoc argument ;
DocData : : argument_doc_from_arginfo ( argument , arginfo ) ;
2014-02-10 02:10:30 +01:00
2022-08-08 14:18:26 +02:00
signal . arguments . push_back ( argument ) ;
}
2020-01-26 15:18:06 +01:00
2022-08-08 14:18:26 +02:00
c . signals . push_back ( signal ) ;
2014-02-10 02:10:30 +01:00
}
2024-02-18 20:41:01 +01:00
c . signals . sort_custom < MethodCompare > ( ) ;
2022-08-08 14:18:26 +02:00
}
List < String > constant_list ;
ClassDB : : get_integer_constant_list ( name , & constant_list , true ) ;
2014-02-10 02:10:30 +01:00
2022-08-08 14:18:26 +02:00
for ( const String & E : constant_list ) {
DocData : : ConstantDoc constant ;
constant . name = E ;
constant . value = itos ( ClassDB : : get_integer_constant ( name , E ) ) ;
constant . is_value_valid = true ;
constant . enumeration = ClassDB : : get_integer_constant_enum ( name , E ) ;
constant . is_bitfield = ClassDB : : is_enum_bitfield ( name , constant . enumeration ) ;
c . constants . push_back ( constant ) ;
2014-02-10 02:10:30 +01:00
}
2022-08-08 14:18:26 +02:00
// Theme items.
{
2023-09-26 16:41:24 +02:00
List < ThemeDB : : ThemeItemBind > theme_items ;
2023-11-11 18:29:42 +01:00
ThemeDB : : get_singleton ( ) - > get_class_items ( cname , & theme_items ) ;
2023-09-26 16:41:24 +02:00
Ref < Theme > default_theme = ThemeDB : : get_singleton ( ) - > get_default_theme ( ) ;
2014-02-10 02:10:30 +01:00
2023-09-26 16:41:24 +02:00
for ( const ThemeDB : : ThemeItemBind & theme_item : theme_items ) {
2022-08-08 14:18:26 +02:00
DocData : : ThemeItemDoc tid ;
2023-09-26 16:41:24 +02:00
tid . name = theme_item . item_name ;
switch ( theme_item . data_type ) {
case Theme : : DATA_TYPE_COLOR :
tid . type = " Color " ;
tid . data_type = " color " ;
break ;
case Theme : : DATA_TYPE_CONSTANT :
tid . type = " int " ;
tid . data_type = " constant " ;
break ;
case Theme : : DATA_TYPE_FONT :
tid . type = " Font " ;
tid . data_type = " font " ;
break ;
case Theme : : DATA_TYPE_FONT_SIZE :
tid . type = " int " ;
tid . data_type = " font_size " ;
break ;
case Theme : : DATA_TYPE_ICON :
tid . type = " Texture2D " ;
tid . data_type = " icon " ;
break ;
case Theme : : DATA_TYPE_STYLEBOX :
tid . type = " StyleBox " ;
tid . data_type = " style " ;
break ;
case Theme : : DATA_TYPE_MAX :
break ; // Can't happen, but silences warning.
}
2021-08-04 18:54:41 +02:00
2023-09-26 16:41:24 +02:00
if ( theme_item . data_type = = Theme : : DATA_TYPE_COLOR | | theme_item . data_type = = Theme : : DATA_TYPE_CONSTANT ) {
tid . default_value = DocData : : get_default_value_string ( default_theme - > get_theme_item ( theme_item . data_type , theme_item . item_name , cname ) ) ;
}
2021-08-04 18:54:41 +02:00
2022-08-08 14:18:26 +02:00
c . theme_properties . push_back ( tid ) ;
}
2021-08-04 18:54:41 +02:00
2022-08-08 14:18:26 +02:00
c . theme_properties . sort ( ) ;
2014-06-30 03:41:02 +02:00
}
2021-12-01 19:02:20 +01:00
2022-08-08 14:18:26 +02:00
classes . pop_front ( ) ;
2014-06-30 03:41:02 +02:00
}
2014-02-10 02:10:30 +01:00
}
2023-10-31 18:27:17 +01:00
if ( p_flags . has_flag ( GENERATE_FLAG_SKIP_BASIC_TYPES ) ) {
return ;
}
2022-08-08 14:18:26 +02:00
// Add a dummy Variant entry.
2016-06-30 00:54:22 +02:00
{
2022-08-08 14:18:26 +02:00
// This allows us to document the concept of Variant even though
// it's not a ClassDB-exposed class.
2020-11-29 04:42:06 +01:00
class_list [ " Variant " ] = DocData : : ClassDoc ( ) ;
2016-06-30 00:54:22 +02:00
class_list [ " Variant " ] . name = " Variant " ;
2023-08-16 14:00:59 +02:00
inheriting [ " " ] . insert ( " Variant " ) ;
2016-06-30 00:54:22 +02:00
}
2022-08-08 14:18:26 +02:00
// Add Variant data types.
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i = = Variant : : NIL ) {
2020-01-26 15:18:06 +01:00
continue ; // Not exposed outside of 'null', should not be in class list.
2020-05-14 16:41:43 +02:00
}
if ( i = = Variant : : OBJECT ) {
2020-01-26 15:18:06 +01:00
continue ; // Use the core type instead.
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
String cname = Variant : : get_type_name ( Variant : : Type ( i ) ) ;
2014-02-10 02:10:30 +01:00
2020-11-29 04:42:06 +01:00
class_list [ cname ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ cname ] ;
2017-05-20 17:38:03 +02:00
c . name = cname ;
2014-02-10 02:10:30 +01:00
2023-08-16 14:00:59 +02:00
inheriting [ " " ] . insert ( cname ) ;
2020-02-19 20:27:19 +01:00
Callable : : CallError cerror ;
2020-11-09 04:19:09 +01:00
Variant v ;
Variant : : construct ( Variant : : Type ( i ) , v , nullptr , 0 , cerror ) ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
List < MethodInfo > method_list ;
v . get_method_list ( & method_list ) ;
Variant : : get_constructor_list ( Variant : : Type ( i ) , & method_list ) ;
2014-02-10 02:10:30 +01:00
2020-11-10 14:13:16 +01:00
for ( int j = 0 ; j < Variant : : OP_AND ; j + + ) { // Showing above 'and' is pretty confusing and there are a lot of variations.
2020-11-09 18:11:15 +01:00
for ( int k = 0 ; k < Variant : : VARIANT_MAX ; k + + ) {
2022-03-14 20:07:26 +01:00
// Prevent generating for comparison with null.
if ( Variant : : Type ( k ) = = Variant : : NIL & & ( Variant : : Operator ( j ) = = Variant : : OP_EQUAL | | Variant : : Operator ( j ) = = Variant : : OP_NOT_EQUAL ) ) {
continue ;
}
2020-11-09 18:11:15 +01:00
Variant : : Type rt = Variant : : get_operator_return_type ( Variant : : Operator ( j ) , Variant : : Type ( i ) , Variant : : Type ( k ) ) ;
2020-11-10 14:13:16 +01:00
if ( rt ! = Variant : : NIL ) { // Has operator.
// Skip String % operator as it's registered separately for each Variant arg type,
// we'll add it manually below.
2022-12-06 03:46:47 +01:00
if ( ( i = = Variant : : STRING | | i = = Variant : : STRING_NAME ) & & Variant : : Operator ( j ) = = Variant : : OP_MODULE ) {
2020-11-10 14:13:16 +01:00
continue ;
}
2020-11-09 18:11:15 +01:00
MethodInfo mi ;
mi . name = " operator " + Variant : : get_operator_name ( Variant : : Operator ( j ) ) ;
mi . return_val . type = rt ;
if ( k ! = Variant : : NIL ) {
PropertyInfo arg ;
arg . name = " right " ;
arg . type = Variant : : Type ( k ) ;
mi . arguments . push_back ( arg ) ;
}
method_list . push_back ( mi ) ;
}
}
}
2022-12-06 03:46:47 +01:00
if ( i = = Variant : : STRING | | i = = Variant : : STRING_NAME ) {
2020-11-10 14:13:16 +01:00
// We skipped % operator above, and we register it manually once for Variant arg type here.
MethodInfo mi ;
mi . name = " operator % " ;
mi . return_val . type = Variant : : STRING ;
PropertyInfo arg ;
arg . name = " right " ;
arg . type = Variant : : NIL ;
arg . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
mi . arguments . push_back ( arg ) ;
method_list . push_back ( mi ) ;
}
2020-11-09 18:11:15 +01:00
if ( Variant : : is_keyed ( Variant : : Type ( i ) ) ) {
MethodInfo mi ;
mi . name = " operator [] " ;
mi . return_val . type = Variant : : NIL ;
mi . return_val . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
PropertyInfo arg ;
arg . name = " key " ;
arg . type = Variant : : NIL ;
arg . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
mi . arguments . push_back ( arg ) ;
method_list . push_back ( mi ) ;
} else if ( Variant : : has_indexing ( Variant : : Type ( i ) ) ) {
MethodInfo mi ;
mi . name = " operator [] " ;
mi . return_val . type = Variant : : get_indexed_element_type ( Variant : : Type ( i ) ) ;
2023-01-18 21:57:47 +01:00
mi . return_val . usage = Variant : : get_indexed_element_usage ( Variant : : Type ( i ) ) ;
2020-11-09 18:11:15 +01:00
PropertyInfo arg ;
arg . name = " index " ;
arg . type = Variant : : INT ;
mi . arguments . push_back ( arg ) ;
method_list . push_back ( mi ) ;
}
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & mi : method_list ) {
2020-11-29 04:42:06 +01:00
DocData : : MethodDoc method ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
method . name = mi . name ;
2014-02-10 02:10:30 +01:00
2019-02-12 21:10:08 +01:00
for ( int j = 0 ; j < mi . arguments . size ( ) ; j + + ) {
PropertyInfo arginfo = mi . arguments [ j ] ;
2020-11-29 04:42:06 +01:00
DocData : : ArgumentDoc ad ;
DocData : : argument_doc_from_arginfo ( ad , mi . arguments [ j ] ) ;
2017-08-29 07:15:46 +02:00
ad . name = arginfo . name ;
2020-03-09 10:51:17 +01:00
int darg_idx = mi . default_arguments . size ( ) - mi . arguments . size ( ) + j ;
if ( darg_idx > = 0 ) {
2022-11-27 08:56:53 +01:00
ad . default_value = DocData : : get_default_value_string ( mi . default_arguments [ darg_idx ] ) ;
2020-03-09 10:51:17 +01:00
}
2014-02-10 02:10:30 +01:00
2017-08-29 07:15:46 +02:00
method . arguments . push_back ( ad ) ;
2017-05-20 17:38:03 +02:00
}
2014-02-10 02:10:30 +01:00
2020-11-29 04:42:06 +01:00
DocData : : return_doc_from_retinfo ( method , mi . return_val ) ;
2020-02-19 20:27:19 +01:00
if ( mi . flags & METHOD_FLAG_VARARG ) {
2021-12-09 10:42:46 +01:00
if ( ! method . qualifiers . is_empty ( ) ) {
2020-02-19 20:27:19 +01:00
method . qualifiers + = " " ;
2020-05-14 16:41:43 +02:00
}
2020-02-19 20:27:19 +01:00
method . qualifiers + = " vararg " ;
2014-02-10 02:10:30 +01:00
}
2021-02-24 14:56:34 +01:00
if ( mi . flags & METHOD_FLAG_CONST ) {
2021-12-09 10:42:46 +01:00
if ( ! method . qualifiers . is_empty ( ) ) {
2021-02-24 14:56:34 +01:00
method . qualifiers + = " " ;
}
method . qualifiers + = " const " ;
}
if ( mi . flags & METHOD_FLAG_STATIC ) {
2021-12-09 10:42:46 +01:00
if ( ! method . qualifiers . is_empty ( ) ) {
2021-02-24 14:56:34 +01:00
method . qualifiers + = " " ;
}
method . qualifiers + = " static " ;
}
2021-09-21 04:49:02 +02:00
if ( method . name = = cname ) {
c . constructors . push_back ( method ) ;
} else if ( method . name . begins_with ( " operator " ) ) {
c . operators . push_back ( method ) ;
} else {
c . methods . push_back ( method ) ;
}
2017-05-20 17:38:03 +02:00
}
2024-02-18 20:41:01 +01:00
c . constructors . sort_custom < ConstructorCompare > ( ) ;
c . operators . sort_custom < OperatorCompare > ( ) ;
c . methods . sort_custom < MethodCompare > ( ) ;
2023-05-04 15:18:56 +02:00
2017-05-20 17:38:03 +02:00
List < PropertyInfo > properties ;
v . get_property_list ( & properties ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & pi : properties ) {
2020-11-29 04:42:06 +01:00
DocData : : PropertyDoc property ;
2017-05-20 17:38:03 +02:00
property . name = pi . name ;
property . type = Variant : : get_type_name ( pi . type ) ;
2022-11-27 08:56:53 +01:00
property . default_value = DocData : : get_default_value_string ( v . get ( pi . name ) ) ;
2014-02-10 02:10:30 +01:00
2017-05-20 17:38:03 +02:00
c . properties . push_back ( property ) ;
}
2014-02-10 02:10:30 +01:00
2024-02-18 20:41:01 +01:00
c . properties . sort ( ) ;
2017-05-20 17:38:03 +02:00
List < StringName > constants ;
2017-12-15 16:43:27 +01:00
Variant : : get_constants_for_type ( Variant : : Type ( i ) , & constants ) ;
2014-02-10 02:10:30 +01:00
2021-07-24 15:46:25 +02:00
for ( const StringName & E : constants ) {
2020-11-29 04:42:06 +01:00
DocData : : ConstantDoc constant ;
2021-07-16 05:45:57 +02:00
constant . name = E ;
Variant value = Variant : : get_constant_value ( Variant : : Type ( i ) , E ) ;
2022-01-18 11:31:15 +01:00
constant . value = value . get_type ( ) = = Variant : : INT ? itos ( value ) : value . get_construct_string ( ) . replace ( " \n " , " " ) ;
2020-05-20 21:49:39 +02:00
constant . is_value_valid = true ;
2017-05-20 17:38:03 +02:00
c . constants . push_back ( constant ) ;
2014-02-10 02:10:30 +01:00
}
}
2022-08-08 14:18:26 +02:00
// Add global API (servers, engine singletons, global constants) and Variant utility functions.
2014-02-10 02:10:30 +01:00
{
2017-11-15 18:45:34 +01:00
String cname = " @GlobalScope " ;
2020-11-29 04:42:06 +01:00
class_list [ cname ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ cname ] ;
2014-02-10 02:10:30 +01:00
c . name = cname ;
2023-08-16 14:00:59 +02:00
inheriting [ " " ] . insert ( cname ) ;
2022-08-08 14:18:26 +02:00
// Global constants.
2020-11-07 23:33:38 +01:00
for ( int i = 0 ; i < CoreConstants : : get_global_constant_count ( ) ; i + + ) {
2020-11-29 04:42:06 +01:00
DocData : : ConstantDoc cd ;
2020-11-07 23:33:38 +01:00
cd . name = CoreConstants : : get_global_constant_name ( i ) ;
2023-01-08 00:55:54 +01:00
cd . is_bitfield = CoreConstants : : is_global_constant_bitfield ( i ) ;
2020-11-07 23:33:38 +01:00
if ( ! CoreConstants : : get_ignore_value_in_docs ( i ) ) {
cd . value = itos ( CoreConstants : : get_global_constant_value ( i ) ) ;
2020-05-20 21:49:39 +02:00
cd . is_value_valid = true ;
} else {
cd . is_value_valid = false ;
}
2020-11-07 23:33:38 +01:00
cd . enumeration = CoreConstants : : get_global_constant_enum ( i ) ;
2014-02-10 02:10:30 +01:00
c . constants . push_back ( cd ) ;
}
2022-08-08 14:18:26 +02:00
// Servers/engine singletons.
2017-11-13 21:46:57 +01:00
List < Engine : : Singleton > singletons ;
Engine : : get_singleton ( ) - > get_singletons ( & singletons ) ;
2014-02-10 02:10:30 +01:00
2022-08-08 14:18:26 +02:00
// FIXME: this is kind of hackish...
2021-07-24 15:46:25 +02:00
for ( const Engine : : Singleton & s : singletons ) {
2020-11-29 04:42:06 +01:00
DocData : : PropertyDoc pd ;
2018-05-11 20:20:27 +02:00
if ( ! s . ptr ) {
continue ;
}
2014-02-10 02:10:30 +01:00
pd . name = s . name ;
2017-01-03 03:03:46 +01:00
pd . type = s . ptr - > get_class ( ) ;
2020-05-14 16:41:43 +02:00
while ( String ( ClassDB : : get_parent_class ( pd . type ) ) ! = " Object " ) {
2017-01-03 03:03:46 +01:00
pd . type = ClassDB : : get_parent_class ( pd . type ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
c . properties . push_back ( pd ) ;
}
2020-11-10 22:31:33 +01:00
2024-02-18 20:41:01 +01:00
c . properties . sort ( ) ;
2022-08-08 14:18:26 +02:00
// Variant utility functions.
2020-11-11 17:16:08 +01:00
List < StringName > utility_functions ;
Variant : : get_utility_function_list ( & utility_functions ) ;
2021-07-24 15:46:25 +02:00
for ( const StringName & E : utility_functions ) {
2020-11-29 04:42:06 +01:00
DocData : : MethodDoc md ;
2021-07-16 05:45:57 +02:00
md . name = E ;
2022-08-08 14:18:26 +02:00
// Utility function's return type.
2021-07-16 05:45:57 +02:00
if ( Variant : : has_utility_function_return_value ( E ) ) {
2020-11-10 22:31:33 +01:00
PropertyInfo pi ;
2021-07-16 05:45:57 +02:00
pi . type = Variant : : get_utility_function_return_type ( E ) ;
2020-11-10 22:31:33 +01:00
if ( pi . type = = Variant : : NIL ) {
pi . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
}
DocData : : ArgumentDoc ad ;
2020-11-29 04:42:06 +01:00
DocData : : argument_doc_from_arginfo ( ad , pi ) ;
2020-11-10 22:31:33 +01:00
md . return_type = ad . type ;
}
2022-08-08 14:18:26 +02:00
// Utility function's arguments.
2021-07-16 05:45:57 +02:00
if ( Variant : : is_utility_function_vararg ( E ) ) {
2020-11-10 22:31:33 +01:00
md . qualifiers = " vararg " ;
} else {
2021-07-16 05:45:57 +02:00
for ( int i = 0 ; i < Variant : : get_utility_function_argument_count ( E ) ; i + + ) {
2020-11-10 22:31:33 +01:00
PropertyInfo pi ;
2021-07-16 05:45:57 +02:00
pi . type = Variant : : get_utility_function_argument_type ( E , i ) ;
pi . name = Variant : : get_utility_function_argument_name ( E , i ) ;
2020-11-10 22:31:33 +01:00
if ( pi . type = = Variant : : NIL ) {
pi . usage = PROPERTY_USAGE_NIL_IS_VARIANT ;
}
DocData : : ArgumentDoc ad ;
2020-11-29 04:42:06 +01:00
DocData : : argument_doc_from_arginfo ( ad , pi ) ;
2020-11-10 22:31:33 +01:00
md . arguments . push_back ( ad ) ;
}
}
c . methods . push_back ( md ) ;
}
2024-02-18 20:41:01 +01:00
c . methods . sort_custom < MethodCompare > ( ) ;
2014-02-10 02:10:30 +01:00
}
2022-08-08 14:18:26 +02:00
// Add scripting language built-ins.
2014-02-10 02:10:30 +01:00
{
2022-08-08 14:18:26 +02:00
// We only add a doc entry for languages which actually define any built-in
// methods, constants, or annotations.
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < ScriptServer : : get_language_count ( ) ; i + + ) {
ScriptLanguage * lang = ScriptServer : : get_language ( i ) ;
String cname = " @ " + lang - > get_name ( ) ;
2020-11-29 04:42:06 +01:00
DocData : : ClassDoc c ;
2014-02-10 02:10:30 +01:00
c . name = cname ;
2023-08-16 14:00:59 +02:00
inheriting [ " " ] . insert ( cname ) ;
2020-06-17 15:06:13 +02:00
// Get functions.
2014-02-10 02:10:30 +01:00
List < MethodInfo > minfo ;
lang - > get_public_functions ( & minfo ) ;
2021-07-24 15:46:25 +02:00
for ( const MethodInfo & mi : minfo ) {
2020-11-29 04:42:06 +01:00
DocData : : MethodDoc md ;
2014-02-10 02:10:30 +01:00
md . name = mi . name ;
2017-08-29 18:16:03 +02:00
if ( mi . flags & METHOD_FLAG_VARARG ) {
2021-12-09 10:42:46 +01:00
if ( ! md . qualifiers . is_empty ( ) ) {
2017-08-29 18:16:03 +02:00
md . qualifiers + = " " ;
2020-05-14 16:41:43 +02:00
}
2017-08-29 18:16:03 +02:00
md . qualifiers + = " vararg " ;
}
2020-11-29 04:42:06 +01:00
DocData : : return_doc_from_retinfo ( md , mi . return_val ) ;
2014-02-10 02:10:30 +01:00
2019-02-12 21:10:08 +01:00
for ( int j = 0 ; j < mi . arguments . size ( ) ; j + + ) {
2020-11-29 04:42:06 +01:00
DocData : : ArgumentDoc ad ;
DocData : : argument_doc_from_arginfo ( ad , mi . arguments [ j ] ) ;
2018-01-30 05:06:19 +01:00
2019-02-12 21:10:08 +01:00
int darg_idx = j - ( mi . arguments . size ( ) - mi . default_arguments . size ( ) ) ;
2018-01-30 05:06:19 +01:00
if ( darg_idx > = 0 ) {
2022-11-27 08:56:53 +01:00
ad . default_value = DocData : : get_default_value_string ( mi . default_arguments [ darg_idx ] ) ;
2018-01-30 05:06:19 +01:00
}
2014-02-10 02:10:30 +01:00
md . arguments . push_back ( ad ) ;
}
c . methods . push_back ( md ) ;
}
2020-06-17 15:06:13 +02:00
// Get constants.
2020-03-17 07:33:00 +01:00
List < Pair < String , Variant > > cinfo ;
2014-02-23 00:28:19 +01:00
lang - > get_public_constants ( & cinfo ) ;
2021-07-24 15:46:25 +02:00
for ( const Pair < String , Variant > & E : cinfo ) {
2020-11-29 04:42:06 +01:00
DocData : : ConstantDoc cd ;
2021-07-16 05:45:57 +02:00
cd . name = E . first ;
cd . value = E . second ;
2020-05-20 21:49:39 +02:00
cd . is_value_valid = true ;
2014-02-23 00:28:19 +01:00
c . constants . push_back ( cd ) ;
}
2020-06-17 15:06:13 +02:00
2022-07-04 17:56:34 +02:00
// Get annotations.
List < MethodInfo > ainfo ;
lang - > get_public_annotations ( & ainfo ) ;
for ( const MethodInfo & ai : ainfo ) {
DocData : : MethodDoc atd ;
atd . name = ai . name ;
if ( ai . flags & METHOD_FLAG_VARARG ) {
if ( ! atd . qualifiers . is_empty ( ) ) {
atd . qualifiers + = " " ;
}
atd . qualifiers + = " vararg " ;
}
DocData : : return_doc_from_retinfo ( atd , ai . return_val ) ;
for ( int j = 0 ; j < ai . arguments . size ( ) ; j + + ) {
DocData : : ArgumentDoc ad ;
DocData : : argument_doc_from_arginfo ( ad , ai . arguments [ j ] ) ;
int darg_idx = j - ( ai . arguments . size ( ) - ai . default_arguments . size ( ) ) ;
if ( darg_idx > = 0 ) {
2022-11-27 08:56:53 +01:00
ad . default_value = DocData : : get_default_value_string ( ai . default_arguments [ darg_idx ] ) ;
2022-07-04 17:56:34 +02:00
}
atd . arguments . push_back ( ad ) ;
}
c . annotations . push_back ( atd ) ;
}
2020-06-17 15:06:13 +02:00
// Skip adding the lang if it doesn't expose anything (e.g. C#).
2022-07-04 17:56:34 +02:00
if ( c . methods . is_empty ( ) & & c . constants . is_empty ( ) & & c . annotations . is_empty ( ) ) {
2020-06-17 15:06:13 +02:00
continue ;
}
2024-02-18 20:41:01 +01:00
c . methods . sort_custom < MethodCompare > ( ) ;
c . annotations . sort_custom < MethodCompare > ( ) ;
2020-06-17 15:06:13 +02:00
class_list [ cname ] = c ;
2014-02-10 02:10:30 +01:00
}
}
}
static Error _parse_methods ( Ref < XMLParser > & parser , Vector < DocData : : MethodDoc > & methods ) {
String section = parser - > get_node_name ( ) ;
String element = section . substr ( 0 , section . length ( ) - 1 ) ;
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
if ( parser - > get_node_name ( ) = = element ) {
DocData : : MethodDoc method ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
method . name = parser - > get_named_attribute_value ( " name " ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > has_attribute ( " qualifiers " ) ) {
2022-11-28 10:00:48 +01:00
method . qualifiers = parser - > get_named_attribute_value ( " qualifiers " ) ;
2020-05-14 16:41:43 +02:00
}
2024-02-12 14:55:02 +01:00
# ifndef DISABLE_DEPRECATED
2022-08-28 08:17:25 +02:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
2022-11-28 10:00:48 +01:00
method . is_deprecated = parser - > get_named_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
2022-11-28 10:00:48 +01:00
method . is_experimental = parser - > get_named_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
2024-02-12 14:55:02 +01:00
# endif
if ( parser - > has_attribute ( " deprecated " ) ) {
method . is_deprecated = true ;
method . deprecated_message = parser - > get_named_attribute_value ( " deprecated " ) ;
}
if ( parser - > has_attribute ( " experimental " ) ) {
method . is_experimental = true ;
method . experimental_message = parser - > get_named_attribute_value ( " experimental " ) ;
}
2023-07-03 18:18:46 +02:00
if ( parser - > has_attribute ( " keywords " ) ) {
method . keywords = parser - > get_named_attribute_value ( " keywords " ) ;
}
2014-02-10 02:10:30 +01:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
String name = parser - > get_node_name ( ) ;
if ( name = = " return " ) {
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
method . return_type = parser - > get_named_attribute_value ( " type " ) ;
2017-09-02 21:52:45 +02:00
if ( parser - > has_attribute ( " enum " ) ) {
2022-11-28 10:00:48 +01:00
method . return_enum = parser - > get_named_attribute_value ( " enum " ) ;
2023-06-15 16:06:22 +02:00
if ( parser - > has_attribute ( " is_bitfield " ) ) {
method . return_is_bitfield = parser - > get_named_attribute_value ( " is_bitfield " ) . to_lower ( ) = = " true " ;
}
2017-09-02 21:52:45 +02:00
}
2021-08-24 20:16:25 +02:00
} else if ( name = = " returns_error " ) {
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " number " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
method . errors_returned . push_back ( parser - > get_named_attribute_value ( " number " ) . to_int ( ) ) ;
2022-08-06 20:11:48 +02:00
} else if ( name = = " param " ) {
2014-02-10 02:10:30 +01:00
DocData : : ArgumentDoc argument ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
argument . name = parser - > get_named_attribute_value ( " name " ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
argument . type = parser - > get_named_attribute_value ( " type " ) ;
2017-08-24 00:10:32 +02:00
if ( parser - > has_attribute ( " enum " ) ) {
2022-11-28 10:00:48 +01:00
argument . enumeration = parser - > get_named_attribute_value ( " enum " ) ;
2023-06-15 16:06:22 +02:00
if ( parser - > has_attribute ( " is_bitfield " ) ) {
argument . is_bitfield = parser - > get_named_attribute_value ( " is_bitfield " ) . to_lower ( ) = = " true " ;
}
2017-08-24 00:10:32 +02:00
}
2014-02-10 02:10:30 +01:00
method . arguments . push_back ( argument ) ;
} else if ( name = = " description " ) {
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2017-11-11 02:11:24 +01:00
method . description = parser - > get_node_data ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = element ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
methods . push_back ( method ) ;
} else {
2021-09-21 04:49:02 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + parser - > get_node_name ( ) + " , expected " + element + " . " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = section ) {
2014-02-10 02:10:30 +01:00
break ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return OK ;
}
2020-11-29 04:42:06 +01:00
Error DocTools : : load_classes ( const String & p_dir ) {
2017-09-12 22:42:36 +02:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : open ( p_dir , & err ) ;
if ( da . is_null ( ) ) {
2014-02-10 02:10:30 +01:00
return err ;
2017-09-12 22:42:36 +02:00
}
2014-02-16 01:16:33 +01:00
2017-09-12 22:42:36 +02:00
da - > list_dir_begin ( ) ;
String path ;
2019-07-25 11:09:57 +02:00
path = da - > get_next ( ) ;
2021-12-09 10:42:46 +01:00
while ( ! path . is_empty ( ) ) {
2019-07-25 11:09:57 +02:00
if ( ! da - > current_is_dir ( ) & & path . ends_with ( " xml " ) ) {
2017-09-12 22:42:36 +02:00
Ref < XMLParser > parser = memnew ( XMLParser ) ;
2022-08-30 02:34:01 +02:00
Error err2 = parser - > open ( p_dir . path_join ( path ) ) ;
2020-05-14 16:41:43 +02:00
if ( err2 ) {
2019-02-12 21:10:08 +01:00
return err2 ;
2020-05-14 16:41:43 +02:00
}
2017-09-12 22:42:36 +02:00
_load ( parser ) ;
}
2019-07-25 11:09:57 +02:00
path = da - > get_next ( ) ;
2017-09-12 22:42:36 +02:00
}
2014-02-10 02:10:30 +01:00
2017-09-12 22:42:36 +02:00
da - > list_dir_end ( ) ;
2014-02-10 02:10:30 +01:00
2017-09-12 22:42:36 +02:00
return OK ;
}
2020-05-14 14:29:06 +02:00
2020-11-29 04:42:06 +01:00
Error DocTools : : erase_classes ( const String & p_dir ) {
2017-09-12 22:42:36 +02:00
Error err ;
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : open ( p_dir , & err ) ;
if ( da . is_null ( ) ) {
2017-09-12 22:42:36 +02:00
return err ;
}
List < String > to_erase ;
da - > list_dir_begin ( ) ;
String path ;
2019-07-25 11:09:57 +02:00
path = da - > get_next ( ) ;
2021-12-09 10:42:46 +01:00
while ( ! path . is_empty ( ) ) {
2019-07-25 11:09:57 +02:00
if ( ! da - > current_is_dir ( ) & & path . ends_with ( " xml " ) ) {
2017-09-12 22:42:36 +02:00
to_erase . push_back ( path ) ;
2014-02-10 02:10:30 +01:00
}
2019-07-25 11:09:57 +02:00
path = da - > get_next ( ) ;
2017-09-12 22:42:36 +02:00
}
da - > list_dir_end ( ) ;
2017-09-12 23:06:26 +02:00
while ( to_erase . size ( ) ) {
2017-09-12 22:42:36 +02:00
da - > remove ( to_erase . front ( ) - > get ( ) ) ;
to_erase . pop_front ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-09-12 22:42:36 +02:00
return OK ;
}
2020-05-14 14:29:06 +02:00
2020-11-29 04:42:06 +01:00
Error DocTools : : _load ( Ref < XMLParser > parser ) {
2017-09-12 22:42:36 +02:00
Error err = OK ;
2014-02-10 02:10:30 +01:00
while ( ( err = parser - > read ( ) ) = = OK ) {
2017-09-12 22:42:36 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT & & parser - > get_node_name ( ) = = " ?xml " ) {
parser - > skip_section ( ) ;
}
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) ! = XMLParser : : NODE_ELEMENT ) {
2014-02-10 02:10:30 +01:00
continue ; //no idea what this may be, but skipping anyway
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( parser - > get_node_name ( ) ! = " class " , ERR_FILE_CORRUPT ) ;
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
String name = parser - > get_named_attribute_value ( " name " ) ;
2020-11-29 04:42:06 +01:00
class_list [ name ] = DocData : : ClassDoc ( ) ;
DocData : : ClassDoc & c = class_list [ name ] ;
2014-02-10 02:10:30 +01:00
c . name = name ;
2020-05-14 16:41:43 +02:00
if ( parser - > has_attribute ( " inherits " ) ) {
2022-11-28 10:00:48 +01:00
c . inherits = parser - > get_named_attribute_value ( " inherits " ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2023-08-16 14:00:59 +02:00
inheriting [ c . inherits ] . insert ( name ) ;
2024-02-12 14:55:02 +01:00
# ifndef DISABLE_DEPRECATED
2022-08-28 08:17:25 +02:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
2022-11-28 10:00:48 +01:00
c . is_deprecated = parser - > get_named_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
2022-11-28 10:00:48 +01:00
c . is_experimental = parser - > get_named_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
2024-02-12 14:55:02 +01:00
# endif
if ( parser - > has_attribute ( " deprecated " ) ) {
c . is_deprecated = true ;
c . deprecated_message = parser - > get_named_attribute_value ( " deprecated " ) ;
}
if ( parser - > has_attribute ( " experimental " ) ) {
c . is_experimental = true ;
c . experimental_message = parser - > get_named_attribute_value ( " experimental " ) ;
}
2022-08-28 08:17:25 +02:00
2023-07-03 18:18:46 +02:00
if ( parser - > has_attribute ( " keywords " ) ) {
c . keywords = parser - > get_named_attribute_value ( " keywords " ) ;
}
2014-02-10 02:10:30 +01:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 21:10:08 +01:00
String name2 = parser - > get_node_name ( ) ;
2014-02-10 02:10:30 +01:00
2019-02-12 21:10:08 +01:00
if ( name2 = = " brief_description " ) {
2014-02-10 02:10:30 +01:00
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2017-11-11 02:11:24 +01:00
c . brief_description = parser - > get_node_data ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " description " ) {
2014-02-10 02:10:30 +01:00
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2017-11-11 02:11:24 +01:00
c . description = parser - > get_node_data ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " tutorials " ) {
2018-06-11 13:35:44 +02:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 21:10:08 +01:00
String name3 = parser - > get_node_name ( ) ;
2018-06-11 13:35:44 +02:00
2019-02-12 21:10:08 +01:00
if ( name3 = = " link " ) {
2020-11-29 04:42:06 +01:00
DocData : : TutorialDoc tutorial ;
2020-06-07 03:26:35 +02:00
if ( parser - > has_attribute ( " title " ) ) {
2022-11-28 10:00:48 +01:00
tutorial . title = parser - > get_named_attribute_value ( " title " ) ;
2020-06-07 03:26:35 +02:00
}
2018-06-11 13:35:44 +02:00
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2020-06-07 03:26:35 +02:00
tutorial . link = parser - > get_node_data ( ) . strip_edges ( ) ;
c . tutorials . push_back ( tutorial ) ;
2020-05-14 16:41:43 +02:00
}
2018-06-11 13:35:44 +02:00
} else {
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2018-06-11 13:35:44 +02:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " tutorials " ) {
2019-09-24 13:34:03 +02:00
break ; // End of <tutorials>.
2020-05-14 16:41:43 +02:00
}
2018-06-11 13:35:44 +02:00
}
2021-09-21 04:49:02 +02:00
} else if ( name2 = = " constructors " ) {
Error err2 = _parse_methods ( parser , c . constructors ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " methods " ) {
Error err2 = _parse_methods ( parser , c . methods ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2021-09-21 04:49:02 +02:00
} else if ( name2 = = " operators " ) {
Error err2 = _parse_methods ( parser , c . operators ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " signals " ) {
Error err2 = _parse_methods ( parser , c . signals ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2022-07-04 17:56:34 +02:00
} else if ( name2 = = " annotations " ) {
Error err2 = _parse_methods ( parser , c . annotations ) ;
ERR_FAIL_COND_V ( err2 , err2 ) ;
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " members " ) {
2014-02-10 02:10:30 +01:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 21:10:08 +01:00
String name3 = parser - > get_node_name ( ) ;
2014-02-10 02:10:30 +01:00
2019-02-12 21:10:08 +01:00
if ( name3 = = " member " ) {
2020-11-29 04:42:06 +01:00
DocData : : PropertyDoc prop2 ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
prop2 . name = parser - > get_named_attribute_value ( " name " ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
prop2 . type = parser - > get_named_attribute_value ( " type " ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > has_attribute ( " setter " ) ) {
2022-11-28 10:00:48 +01:00
prop2 . setter = parser - > get_named_attribute_value ( " setter " ) ;
2020-05-14 16:41:43 +02:00
}
if ( parser - > has_attribute ( " getter " ) ) {
2022-11-28 10:00:48 +01:00
prop2 . getter = parser - > get_named_attribute_value ( " getter " ) ;
2020-05-14 16:41:43 +02:00
}
if ( parser - > has_attribute ( " enum " ) ) {
2022-11-28 10:00:48 +01:00
prop2 . enumeration = parser - > get_named_attribute_value ( " enum " ) ;
2023-06-15 16:06:22 +02:00
if ( parser - > has_attribute ( " is_bitfield " ) ) {
prop2 . is_bitfield = parser - > get_named_attribute_value ( " is_bitfield " ) . to_lower ( ) = = " true " ;
}
2020-05-14 16:41:43 +02:00
}
2024-02-12 14:55:02 +01:00
# ifndef DISABLE_DEPRECATED
2022-08-28 08:17:25 +02:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
2022-11-28 10:00:48 +01:00
prop2 . is_deprecated = parser - > get_named_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
2022-11-28 10:00:48 +01:00
prop2 . is_experimental = parser - > get_named_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
2024-02-12 14:55:02 +01:00
# endif
if ( parser - > has_attribute ( " deprecated " ) ) {
prop2 . is_deprecated = true ;
prop2 . deprecated_message = parser - > get_named_attribute_value ( " deprecated " ) ;
}
if ( parser - > has_attribute ( " experimental " ) ) {
prop2 . is_experimental = true ;
prop2 . experimental_message = parser - > get_named_attribute_value ( " experimental " ) ;
}
2023-07-03 18:18:46 +02:00
if ( parser - > has_attribute ( " keywords " ) ) {
prop2 . keywords = parser - > get_named_attribute_value ( " keywords " ) ;
}
2019-09-24 13:34:03 +02:00
if ( ! parser - > is_empty ( ) ) {
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2019-09-24 13:34:03 +02:00
prop2 . description = parser - > get_node_data ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-09-24 13:34:03 +02:00
}
2019-02-12 21:10:08 +01:00
c . properties . push_back ( prop2 ) ;
2014-02-10 02:10:30 +01:00
} else {
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " members " ) {
2019-09-24 13:34:03 +02:00
break ; // End of <members>.
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " theme_items " ) {
2014-06-30 03:41:02 +02:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 21:10:08 +01:00
String name3 = parser - > get_node_name ( ) ;
2014-06-30 03:41:02 +02:00
2019-02-12 21:10:08 +01:00
if ( name3 = = " theme_item " ) {
2021-08-04 18:54:41 +02:00
DocData : : ThemeItemDoc prop2 ;
2014-06-30 03:41:02 +02:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
prop2 . name = parser - > get_named_attribute_value ( " name " ) ;
2014-06-30 03:41:02 +02:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " type " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
prop2 . type = parser - > get_named_attribute_value ( " type " ) ;
2021-08-04 18:54:41 +02:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " data_type " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
prop2 . data_type = parser - > get_named_attribute_value ( " data_type " ) ;
2023-07-03 18:18:46 +02:00
if ( parser - > has_attribute ( " keywords " ) ) {
prop2 . keywords = parser - > get_named_attribute_value ( " keywords " ) ;
}
2019-09-24 13:34:03 +02:00
if ( ! parser - > is_empty ( ) ) {
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2019-09-24 13:34:03 +02:00
prop2 . description = parser - > get_node_data ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-09-24 13:34:03 +02:00
}
2019-02-12 21:10:08 +01:00
c . theme_properties . push_back ( prop2 ) ;
2014-06-30 03:41:02 +02:00
} else {
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2014-06-30 03:41:02 +02:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " theme_items " ) {
2019-09-24 13:34:03 +02:00
break ; // End of <theme_items>.
2020-05-14 16:41:43 +02:00
}
2014-06-30 03:41:02 +02:00
}
2019-02-12 21:10:08 +01:00
} else if ( name2 = = " constants " ) {
2014-02-10 02:10:30 +01:00
while ( parser - > read ( ) = = OK ) {
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT ) {
2019-02-12 21:10:08 +01:00
String name3 = parser - > get_node_name ( ) ;
2014-02-10 02:10:30 +01:00
2019-02-12 21:10:08 +01:00
if ( name3 = = " constant " ) {
2020-11-29 04:42:06 +01:00
DocData : : ConstantDoc constant2 ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " name " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
constant2 . name = parser - > get_named_attribute_value ( " name " ) ;
2014-02-10 02:10:30 +01:00
ERR_FAIL_COND_V ( ! parser - > has_attribute ( " value " ) , ERR_FILE_CORRUPT ) ;
2022-11-28 10:00:48 +01:00
constant2 . value = parser - > get_named_attribute_value ( " value " ) ;
2020-05-20 21:49:39 +02:00
constant2 . is_value_valid = true ;
2017-08-24 00:10:32 +02:00
if ( parser - > has_attribute ( " enum " ) ) {
2022-11-28 10:00:48 +01:00
constant2 . enumeration = parser - > get_named_attribute_value ( " enum " ) ;
2023-06-15 16:06:22 +02:00
if ( parser - > has_attribute ( " is_bitfield " ) ) {
constant2 . is_bitfield = parser - > get_named_attribute_value ( " is_bitfield " ) . to_lower ( ) = = " true " ;
}
2022-06-24 11:16:37 +02:00
}
2024-02-12 14:55:02 +01:00
# ifndef DISABLE_DEPRECATED
2022-08-28 08:17:25 +02:00
if ( parser - > has_attribute ( " is_deprecated " ) ) {
2022-11-28 10:00:48 +01:00
constant2 . is_deprecated = parser - > get_named_attribute_value ( " is_deprecated " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
if ( parser - > has_attribute ( " is_experimental " ) ) {
2022-11-28 10:00:48 +01:00
constant2 . is_experimental = parser - > get_named_attribute_value ( " is_experimental " ) . to_lower ( ) = = " true " ;
2022-08-28 08:17:25 +02:00
}
2024-02-12 14:55:02 +01:00
# endif
if ( parser - > has_attribute ( " deprecated " ) ) {
constant2 . is_deprecated = true ;
constant2 . deprecated_message = parser - > get_named_attribute_value ( " deprecated " ) ;
}
if ( parser - > has_attribute ( " experimental " ) ) {
constant2 . is_experimental = true ;
constant2 . experimental_message = parser - > get_named_attribute_value ( " experimental " ) ;
}
2023-07-03 18:18:46 +02:00
if ( parser - > has_attribute ( " keywords " ) ) {
constant2 . keywords = parser - > get_named_attribute_value ( " keywords " ) ;
}
2019-09-24 13:34:03 +02:00
if ( ! parser - > is_empty ( ) ) {
parser - > read ( ) ;
2020-05-14 16:41:43 +02:00
if ( parser - > get_node_type ( ) = = XMLParser : : NODE_TEXT ) {
2019-09-24 13:34:03 +02:00
constant2 . description = parser - > get_node_data ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-09-24 13:34:03 +02:00
}
2019-02-12 21:10:08 +01:00
c . constants . push_back ( constant2 ) ;
2014-02-10 02:10:30 +01:00
} else {
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name3 + " . " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " constants " ) {
2019-09-24 13:34:03 +02:00
break ; // End of <constants>.
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
} else {
2019-08-15 04:57:49 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CORRUPT , " Invalid tag in doc file: " + name2 + " . " ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 16:41:43 +02:00
} else if ( parser - > get_node_type ( ) = = XMLParser : : NODE_ELEMENT_END & & parser - > get_node_name ( ) = = " class " ) {
2019-09-24 13:34:03 +02:00
break ; // End of <class>.
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2024-02-18 20:41:01 +01:00
// Sort loaded constants for merging.
c . constants . sort ( ) ;
2014-02-10 02:10:30 +01:00
}
return OK ;
}
2022-03-23 10:08:58 +01:00
static void _write_string ( Ref < FileAccess > f , int p_tablevel , const String & p_string ) {
2021-12-09 10:42:46 +01:00
if ( p_string . is_empty ( ) ) {
2017-11-11 02:11:24 +01:00
return ;
2020-05-14 16:41:43 +02:00
}
2023-01-29 00:52:05 +01:00
String tab = String ( " \t " ) . repeat ( p_tablevel ) ;
2014-02-10 02:10:30 +01:00
f - > store_string ( tab + p_string + " \n " ) ;
}
2022-03-23 10:08:58 +01:00
static void _write_method_doc ( Ref < FileAccess > f , const String & p_name , Vector < DocData : : MethodDoc > & p_method_docs ) {
2021-09-21 02:37:32 +02:00
if ( ! p_method_docs . is_empty ( ) ) {
_write_string ( f , 1 , " < " + p_name + " s> " ) ;
for ( int i = 0 ; i < p_method_docs . size ( ) ; i + + ) {
const DocData : : MethodDoc & m = p_method_docs [ i ] ;
2023-07-03 18:18:46 +02:00
String additional_attributes ;
2021-12-09 10:42:46 +01:00
if ( ! m . qualifiers . is_empty ( ) ) {
2023-07-03 18:18:46 +02:00
additional_attributes + = " qualifiers= \" " + m . qualifiers . xml_escape ( true ) + " \" " ;
2021-09-21 02:37:32 +02:00
}
2022-08-28 08:17:25 +02:00
if ( m . is_deprecated ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " deprecated= \" " + m . deprecated_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
if ( m . is_experimental ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " experimental= \" " + m . experimental_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
2023-07-03 18:18:46 +02:00
if ( ! m . keywords . is_empty ( ) ) {
additional_attributes + = String ( " keywords= \" " ) + m . keywords . xml_escape ( true ) + " \" " ;
}
2022-08-28 08:17:25 +02:00
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " < " + p_name + " name= \" " + m . name . xml_escape ( true ) + " \" " + additional_attributes + " > " ) ;
2021-09-21 02:37:32 +02:00
2021-12-09 10:42:46 +01:00
if ( ! m . return_type . is_empty ( ) ) {
2021-09-21 02:37:32 +02:00
String enum_text ;
2021-12-09 10:42:46 +01:00
if ( ! m . return_enum . is_empty ( ) ) {
2024-02-12 14:55:02 +01:00
enum_text = " enum= \" " + m . return_enum . xml_escape ( true ) + " \" " ;
2023-06-15 16:06:22 +02:00
if ( m . return_is_bitfield ) {
enum_text + = " is_bitfield= \" true \" " ;
}
2021-09-21 02:37:32 +02:00
}
2023-04-26 23:32:03 +02:00
_write_string ( f , 3 , " <return type= \" " + m . return_type . xml_escape ( true ) + " \" " + enum_text + " /> " ) ;
2021-09-21 02:37:32 +02:00
}
if ( m . errors_returned . size ( ) > 0 ) {
for ( int j = 0 ; j < m . errors_returned . size ( ) ; j + + ) {
_write_string ( f , 3 , " <returns_error number= \" " + itos ( m . errors_returned [ j ] ) + " \" /> " ) ;
}
}
for ( int j = 0 ; j < m . arguments . size ( ) ; j + + ) {
const DocData : : ArgumentDoc & a = m . arguments [ j ] ;
String enum_text ;
2021-12-09 10:42:46 +01:00
if ( ! a . enumeration . is_empty ( ) ) {
2024-02-12 14:55:02 +01:00
enum_text = " enum= \" " + a . enumeration . xml_escape ( true ) + " \" " ;
2023-06-15 16:06:22 +02:00
if ( a . is_bitfield ) {
enum_text + = " is_bitfield= \" true \" " ;
}
2021-09-21 02:37:32 +02:00
}
2021-12-09 10:42:46 +01:00
if ( ! a . default_value . is_empty ( ) ) {
2024-02-12 14:55:02 +01:00
_write_string ( f , 3 , " <param index= \" " + itos ( j ) + " \" name= \" " + a . name . xml_escape ( true ) + " \" type= \" " + a . type . xml_escape ( true ) + " \" " + enum_text + " default= \" " + a . default_value . xml_escape ( true ) + " \" /> " ) ;
2021-09-21 02:37:32 +02:00
} else {
2024-02-12 14:55:02 +01:00
_write_string ( f , 3 , " <param index= \" " + itos ( j ) + " \" name= \" " + a . name . xml_escape ( true ) + " \" type= \" " + a . type . xml_escape ( true ) + " \" " + enum_text + " /> " ) ;
2021-09-21 02:37:32 +02:00
}
}
_write_string ( f , 3 , " <description> " ) ;
2021-12-15 16:01:06 +01:00
_write_string ( f , 4 , _translate_doc_string ( m . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2021-09-21 02:37:32 +02:00
_write_string ( f , 3 , " </description> " ) ;
_write_string ( f , 2 , " </ " + p_name + " > " ) ;
}
_write_string ( f , 1 , " </ " + p_name + " s> " ) ;
}
}
2023-04-26 23:32:03 +02:00
Error DocTools : : save_classes ( const String & p_default_path , const HashMap < String , String > & p_class_path , bool p_include_xml_schema ) {
2021-08-09 22:13:42 +02:00
for ( KeyValue < String , DocData : : ClassDoc > & E : class_list ) {
DocData : : ClassDoc & c = E . value ;
2017-03-05 16:44:50 +01:00
2017-09-12 22:42:36 +02:00
String save_path ;
if ( p_class_path . has ( c . name ) ) {
2017-09-12 23:06:26 +02:00
save_path = p_class_path [ c . name ] ;
2017-09-12 22:42:36 +02:00
} else {
2017-09-12 23:06:26 +02:00
save_path = p_default_path ;
2017-09-12 22:42:36 +02:00
}
Error err ;
2023-04-26 23:32:03 +02:00
String save_file = save_path . path_join ( c . name . replace ( " \" " , " " ) . replace ( " / " , " -- " ) + " .xml " ) ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( save_file , FileAccess : : WRITE , & err ) ;
2019-08-15 04:57:49 +02:00
ERR_CONTINUE_MSG ( err ! = OK , " Can't write doc file: " + save_file + " . " ) ;
2017-09-12 22:42:36 +02:00
_write_string ( f , 0 , " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> " ) ;
2023-04-26 23:32:03 +02:00
String header = " <class name= \" " + c . name . xml_escape ( true ) + " \" " ;
2021-12-09 10:42:46 +01:00
if ( ! c . inherits . is_empty ( ) ) {
2023-04-26 23:32:03 +02:00
header + = " inherits= \" " + c . inherits . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
if ( c . is_deprecated ) {
2024-02-12 14:55:02 +01:00
header + = " deprecated= \" " + c . deprecated_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
if ( c . is_experimental ) {
2024-02-12 14:55:02 +01:00
header + = " experimental= \" " + c . experimental_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
2020-05-14 16:41:43 +02:00
}
2023-07-03 18:18:46 +02:00
if ( ! c . keywords . is_empty ( ) ) {
header + = String ( " keywords= \" " ) + c . keywords . xml_escape ( true ) + " \" " ;
}
2023-04-26 23:32:03 +02:00
if ( p_include_xml_schema ) {
// Reference the XML schema so editors can provide error checking.
// Modules are nested deep, so change the path to reference the same schema everywhere.
const String schema_path = save_path . find ( " modules/ " ) ! = - 1 ? " ../../../doc/class.xsd " : " ../class.xsd " ;
header + = vformat (
R " ( xmlns:xsi= " http : //www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s")",
schema_path ) ;
}
header + = " > " ;
2014-02-10 02:10:30 +01:00
_write_string ( f , 0 , header ) ;
2020-01-26 16:01:49 +01:00
2014-02-10 02:10:30 +01:00
_write_string ( f , 1 , " <brief_description> " ) ;
2021-12-15 16:01:06 +01:00
_write_string ( f , 2 , _translate_doc_string ( c . brief_description ) . strip_edges ( ) . xml_escape ( ) ) ;
2014-02-10 02:10:30 +01:00
_write_string ( f , 1 , " </brief_description> " ) ;
2020-01-26 16:01:49 +01:00
2014-02-10 02:10:30 +01:00
_write_string ( f , 1 , " <description> " ) ;
2021-12-15 16:01:06 +01:00
_write_string ( f , 2 , _translate_doc_string ( c . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2014-02-10 02:10:30 +01:00
_write_string ( f , 1 , " </description> " ) ;
2020-01-26 16:01:49 +01:00
2017-09-12 22:42:36 +02:00
_write_string ( f , 1 , " <tutorials> " ) ;
2018-06-11 13:35:44 +02:00
for ( int i = 0 ; i < c . tutorials . size ( ) ; i + + ) {
2020-11-29 04:42:06 +01:00
DocData : : TutorialDoc tutorial = c . tutorials . get ( i ) ;
2024-02-12 14:55:02 +01:00
String title_attribute = ( ! tutorial . title . is_empty ( ) ) ? " title= \" " + _translate_doc_string ( tutorial . title ) . xml_escape ( true ) + " \" " : " " ;
2020-06-07 03:26:35 +02:00
_write_string ( f , 2 , " <link " + title_attribute + " > " + tutorial . link . xml_escape ( ) + " </link> " ) ;
2018-06-11 13:35:44 +02:00
}
2017-09-12 22:42:36 +02:00
_write_string ( f , 1 , " </tutorials> " ) ;
2020-01-26 16:01:49 +01:00
2021-09-21 04:49:02 +02:00
_write_method_doc ( f , " constructor " , c . constructors ) ;
2021-09-21 02:37:32 +02:00
_write_method_doc ( f , " method " , c . methods ) ;
2014-02-10 02:10:30 +01:00
2021-09-21 02:37:32 +02:00
if ( ! c . properties . is_empty ( ) ) {
2014-02-10 02:10:30 +01:00
_write_string ( f , 1 , " <members> " ) ;
for ( int i = 0 ; i < c . properties . size ( ) ; i + + ) {
2019-06-01 15:42:22 +02:00
String additional_attributes ;
2021-12-09 10:42:46 +01:00
if ( ! c . properties [ i ] . enumeration . is_empty ( ) ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " enum= \" " + c . properties [ i ] . enumeration . xml_escape ( true ) + " \" " ;
2023-06-15 16:06:22 +02:00
if ( c . properties [ i ] . is_bitfield ) {
additional_attributes + = " is_bitfield= \" true \" " ;
}
2019-06-01 15:42:22 +02:00
}
2021-12-09 10:42:46 +01:00
if ( ! c . properties [ i ] . default_value . is_empty ( ) ) {
2019-06-01 15:42:22 +02:00
additional_attributes + = " default= \" " + c . properties [ i ] . default_value . xml_escape ( true ) + " \" " ;
2017-08-24 00:10:32 +02:00
}
2022-08-28 08:17:25 +02:00
if ( c . properties [ i ] . is_deprecated ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " deprecated= \" " + c . properties [ i ] . deprecated_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
if ( c . properties [ i ] . is_experimental ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " experimental= \" " + c . properties [ i ] . experimental_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
2023-07-03 18:18:46 +02:00
if ( ! c . properties [ i ] . keywords . is_empty ( ) ) {
additional_attributes + = String ( " keywords= \" " ) + c . properties [ i ] . keywords . xml_escape ( true ) + " \" " ;
}
2019-09-03 12:42:34 +02:00
2020-11-29 04:42:06 +01:00
const DocData : : PropertyDoc & p = c . properties [ i ] ;
2019-09-03 12:42:34 +02:00
if ( c . properties [ i ] . overridden ) {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <member name= \" " + p . name . xml_escape ( true ) + " \" type= \" " + p . type . xml_escape ( true ) + " \" setter= \" " + p . setter . xml_escape ( true ) + " \" getter= \" " + p . getter . xml_escape ( true ) + " \" overrides= \" " + p . overrides . xml_escape ( true ) + " \" " + additional_attributes + " /> " ) ;
2019-09-03 12:42:34 +02:00
} else {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <member name= \" " + p . name . xml_escape ( true ) + " \" type= \" " + p . type . xml_escape ( true ) + " \" setter= \" " + p . setter . xml_escape ( true ) + " \" getter= \" " + p . getter . xml_escape ( true ) + " \" " + additional_attributes + " > " ) ;
2021-12-15 16:01:06 +01:00
_write_string ( f , 3 , _translate_doc_string ( p . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2019-09-03 12:42:34 +02:00
_write_string ( f , 2 , " </member> " ) ;
}
2014-02-10 02:10:30 +01:00
}
_write_string ( f , 1 , " </members> " ) ;
}
2021-09-21 02:37:32 +02:00
_write_method_doc ( f , " signal " , c . signals ) ;
2014-02-10 02:10:30 +01:00
2021-09-21 02:37:32 +02:00
if ( ! c . constants . is_empty ( ) ) {
_write_string ( f , 1 , " <constants> " ) ;
for ( int i = 0 ; i < c . constants . size ( ) ; i + + ) {
const DocData : : ConstantDoc & k = c . constants [ i ] ;
2022-08-28 08:17:25 +02:00
String additional_attributes ;
if ( c . constants [ i ] . is_deprecated ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " deprecated= \" " + c . constants [ i ] . deprecated_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
if ( c . constants [ i ] . is_experimental ) {
2024-02-12 14:55:02 +01:00
additional_attributes + = " experimental= \" " + c . constants [ i ] . experimental_message . xml_escape ( true ) + " \" " ;
2022-08-28 08:17:25 +02:00
}
2023-07-03 18:18:46 +02:00
if ( ! c . constants [ i ] . keywords . is_empty ( ) ) {
additional_attributes + = String ( " keywords= \" " ) + c . constants [ i ] . keywords . xml_escape ( true ) + " \" " ;
}
2022-08-28 08:17:25 +02:00
2021-09-21 02:37:32 +02:00
if ( k . is_value_valid ) {
2021-12-09 10:42:46 +01:00
if ( ! k . enumeration . is_empty ( ) ) {
2022-06-24 11:16:37 +02:00
if ( k . is_bitfield ) {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <constant name= \" " + k . name . xml_escape ( true ) + " \" value= \" " + k . value . xml_escape ( true ) + " \" enum= \" " + k . enumeration . xml_escape ( true ) + " \" is_bitfield= \" true \" " + additional_attributes + " > " ) ;
2022-06-24 11:16:37 +02:00
} else {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <constant name= \" " + k . name . xml_escape ( true ) + " \" value= \" " + k . value . xml_escape ( true ) + " \" enum= \" " + k . enumeration . xml_escape ( true ) + " \" " + additional_attributes + " > " ) ;
2022-06-24 11:16:37 +02:00
}
2021-09-21 02:37:32 +02:00
} else {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <constant name= \" " + k . name . xml_escape ( true ) + " \" value= \" " + k . value . xml_escape ( true ) + " \" " + additional_attributes + " > " ) ;
2021-09-21 02:37:32 +02:00
}
2020-05-20 21:49:39 +02:00
} else {
2021-12-09 10:42:46 +01:00
if ( ! k . enumeration . is_empty ( ) ) {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <constant name= \" " + k . name . xml_escape ( true ) + " \" value= \" platform-dependent \" enum= \" " + k . enumeration . xml_escape ( true ) + " \" " + additional_attributes + " > " ) ;
2021-09-21 02:37:32 +02:00
} else {
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <constant name= \" " + k . name . xml_escape ( true ) + " \" value= \" platform-dependent \" " + additional_attributes + " > " ) ;
2021-09-21 02:37:32 +02:00
}
2020-05-20 21:49:39 +02:00
}
2021-12-15 16:01:06 +01:00
_write_string ( f , 3 , _translate_doc_string ( k . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2021-09-21 02:37:32 +02:00
_write_string ( f , 2 , " </constant> " ) ;
2017-08-24 00:10:32 +02:00
}
2014-02-10 02:10:30 +01:00
2021-09-21 02:37:32 +02:00
_write_string ( f , 1 , " </constants> " ) ;
}
2014-06-30 03:41:02 +02:00
2022-07-04 17:56:34 +02:00
_write_method_doc ( f , " annotation " , c . annotations ) ;
2021-09-21 02:37:32 +02:00
if ( ! c . theme_properties . is_empty ( ) ) {
2014-10-15 00:44:41 +02:00
_write_string ( f , 1 , " <theme_items> " ) ;
2014-06-30 03:41:02 +02:00
for ( int i = 0 ; i < c . theme_properties . size ( ) ; i + + ) {
2021-08-04 18:54:41 +02:00
const DocData : : ThemeItemDoc & ti = c . theme_properties [ i ] ;
2019-06-01 15:42:22 +02:00
2023-07-03 18:18:46 +02:00
String additional_attributes ;
2021-12-09 10:42:46 +01:00
if ( ! ti . default_value . is_empty ( ) ) {
2023-07-03 18:18:46 +02:00
additional_attributes + = String ( " default= \" " ) + ti . default_value . xml_escape ( true ) + " \" " ;
2020-05-14 16:41:43 +02:00
}
2023-07-03 18:18:46 +02:00
if ( ! ti . keywords . is_empty ( ) ) {
additional_attributes + = String ( " keywords= \" " ) + ti . keywords . xml_escape ( true ) + " \" " ;
}
2024-02-12 14:55:02 +01:00
_write_string ( f , 2 , " <theme_item name= \" " + ti . name . xml_escape ( true ) + " \" data_type= \" " + ti . data_type . xml_escape ( true ) + " \" type= \" " + ti . type . xml_escape ( true ) + " \" " + additional_attributes + " > " ) ;
2019-06-01 15:42:22 +02:00
2021-12-15 16:01:06 +01:00
_write_string ( f , 3 , _translate_doc_string ( ti . description ) . strip_edges ( ) . xml_escape ( ) ) ;
2019-06-01 15:42:22 +02:00
2014-06-30 03:41:02 +02:00
_write_string ( f , 2 , " </theme_item> " ) ;
}
2014-10-15 00:44:41 +02:00
_write_string ( f , 1 , " </theme_items> " ) ;
2014-06-30 03:41:02 +02:00
}
2021-09-21 04:49:02 +02:00
_write_method_doc ( f , " operator " , c . operators ) ;
2014-10-15 00:44:41 +02:00
_write_string ( f , 0 , " </class> " ) ;
2014-02-10 02:10:30 +01:00
}
return OK ;
}
2020-11-29 04:42:06 +01:00
Error DocTools : : load_compressed ( const uint8_t * p_data , int p_compressed_size , int p_uncompressed_size ) {
2014-02-10 02:10:30 +01:00
Vector < uint8_t > data ;
data . resize ( p_uncompressed_size ) ;
2022-02-18 14:36:13 +01:00
int ret = Compression : : decompress ( data . ptrw ( ) , p_uncompressed_size , p_data , p_compressed_size , Compression : : MODE_DEFLATE ) ;
ERR_FAIL_COND_V_MSG ( ret = = - 1 , ERR_FILE_CORRUPT , " Compressed file is corrupt. " ) ;
2014-02-10 02:10:30 +01:00
class_list . clear ( ) ;
2014-02-16 01:16:33 +01:00
Ref < XMLParser > parser = memnew ( XMLParser ) ;
Error err = parser - > open_buffer ( data ) ;
2020-05-14 16:41:43 +02:00
if ( err ) {
2014-02-16 01:16:33 +01:00
return err ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2014-02-16 01:16:33 +01:00
_load ( parser ) ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2023-10-19 00:50:30 +02:00
Error DocTools : : load_xml ( const uint8_t * p_data , int p_size ) {
Ref < XMLParser > parser = memnew ( XMLParser ) ;
Error err = parser - > _open_buffer ( p_data , p_size ) ;
if ( err ) {
return err ;
}
_load ( parser ) ;
return OK ;
}