mirror of
https://github.com/godotengine/godot.git
synced 2024-11-12 23:24:26 +00:00
Added doc gen for structs.
This commit is contained in:
parent
e4e024ab88
commit
16ccd3782f
@ -127,6 +127,30 @@ void DocData::property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_propert
|
||||
p_property.overridden = false;
|
||||
}
|
||||
|
||||
void DocData::struct_doc_from_structinfo(DocData::StructDoc &p_struct, const StructInfo &p_structinfo) {
|
||||
p_struct.name = p_structinfo.name;
|
||||
// TODO: what about description?
|
||||
|
||||
p_struct.properties.resize(p_structinfo.count);
|
||||
for (int i = 0; i < p_structinfo.count; i++) {
|
||||
PropertyDoc property_doc;
|
||||
property_doc.name = p_structinfo.names[i];
|
||||
Variant::Type type = p_structinfo.types[i];
|
||||
if (type == Variant::OBJECT) {
|
||||
property_doc.type = p_structinfo.class_names[i];
|
||||
} else if (type == Variant::NIL) {
|
||||
property_doc.type = "Variant";
|
||||
} else {
|
||||
property_doc.type = Variant::get_type_name(type);
|
||||
}
|
||||
if (type != Variant::OBJECT) {
|
||||
property_doc.default_value = get_default_value_string(p_structinfo.default_values[i]);
|
||||
}
|
||||
// TODO: what about description?
|
||||
p_struct.properties.write[i] = property_doc;
|
||||
}
|
||||
}
|
||||
|
||||
void DocData::method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc) {
|
||||
p_method.name = p_methodinfo.name;
|
||||
p_method.description = p_desc;
|
||||
|
@ -698,6 +698,67 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct StructDoc {
|
||||
String name;
|
||||
String description;
|
||||
Vector<PropertyDoc> properties;
|
||||
bool is_deprecated = false;
|
||||
bool is_experimental = false;
|
||||
bool operator<(const StructDoc &p_struct) const {
|
||||
return name < p_struct.name;
|
||||
}
|
||||
static StructDoc from_dict(const Dictionary &p_dict) {
|
||||
StructDoc doc;
|
||||
|
||||
if (p_dict.has("name")) {
|
||||
doc.name = p_dict["name"];
|
||||
}
|
||||
|
||||
if (p_dict.has("description")) {
|
||||
doc.description = p_dict["description"];
|
||||
}
|
||||
|
||||
Array properties;
|
||||
if (p_dict.has("properties")) {
|
||||
properties = p_dict["properties"];
|
||||
}
|
||||
for (int i = 0; i < properties.size(); i++) {
|
||||
doc.properties.push_back(PropertyDoc::from_dict(properties[i]));
|
||||
}
|
||||
|
||||
if (p_dict.has("is_experimental")) {
|
||||
doc.is_experimental = p_dict["is_experimental"];
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
static Dictionary to_dict(const StructDoc &p_doc) {
|
||||
Dictionary dict;
|
||||
|
||||
if (!p_doc.name.is_empty()) {
|
||||
dict["name"] = p_doc.name;
|
||||
}
|
||||
|
||||
if (!p_doc.description.is_empty()) {
|
||||
dict["description"] = p_doc.description;
|
||||
}
|
||||
|
||||
if (!p_doc.properties.is_empty()) {
|
||||
Array properties;
|
||||
for (int i = 0; i < p_doc.properties.size(); i++) {
|
||||
properties.push_back(PropertyDoc::to_dict(p_doc.properties[i]));
|
||||
}
|
||||
dict["properties"] = properties;
|
||||
}
|
||||
|
||||
dict["is_deprecated"] = p_doc.is_deprecated;
|
||||
|
||||
dict["is_experimental"] = p_doc.is_experimental;
|
||||
|
||||
return dict;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassDoc {
|
||||
String name;
|
||||
String inherits;
|
||||
@ -713,6 +774,7 @@ public:
|
||||
HashMap<String, EnumDoc> enums;
|
||||
Vector<PropertyDoc> properties;
|
||||
Vector<MethodDoc> annotations;
|
||||
Vector<StructDoc> structs;
|
||||
Vector<ThemeItemDoc> theme_properties;
|
||||
bool is_deprecated = false;
|
||||
String deprecated_message;
|
||||
@ -818,6 +880,14 @@ public:
|
||||
doc.annotations.push_back(MethodDoc::from_dict(annotations[i]));
|
||||
}
|
||||
|
||||
Array structs;
|
||||
if (p_dict.has("structs")) {
|
||||
structs = p_dict["structs"];
|
||||
}
|
||||
for (int i = 0; i < structs.size(); i++) {
|
||||
doc.structs.push_back(StructDoc::from_dict(structs[i]));
|
||||
}
|
||||
|
||||
Array theme_properties;
|
||||
if (p_dict.has("theme_properties")) {
|
||||
theme_properties = p_dict["theme_properties"];
|
||||
@ -947,6 +1017,13 @@ public:
|
||||
dict["annotations"] = annotations;
|
||||
}
|
||||
|
||||
if (!p_doc.structs.is_empty()) {
|
||||
Array structs;
|
||||
for (int i = 0; i < p_doc.structs.size(); i++) {
|
||||
structs.push_back(StructDoc::to_dict(p_doc.structs[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (!p_doc.theme_properties.is_empty()) {
|
||||
Array theme_properties;
|
||||
for (int i = 0; i < p_doc.theme_properties.size(); i++) {
|
||||
@ -982,6 +1059,7 @@ public:
|
||||
static void return_doc_from_retinfo(DocData::MethodDoc &p_method, const PropertyInfo &p_retinfo);
|
||||
static void argument_doc_from_arginfo(DocData::ArgumentDoc &p_argument, const PropertyInfo &p_arginfo);
|
||||
static void property_doc_from_scriptmemberinfo(DocData::PropertyDoc &p_property, const ScriptMemberInfo &p_memberinfo);
|
||||
static void struct_doc_from_structinfo(DocData::StructDoc &p_struct, const StructInfo &p_structinfo);
|
||||
static void method_doc_from_methodinfo(DocData::MethodDoc &p_method, const MethodInfo &p_methodinfo, const String &p_desc);
|
||||
static void constant_doc_from_variant(DocData::ConstantDoc &p_const, const StringName &p_name, const Variant &p_value, const String &p_desc);
|
||||
static void signal_doc_from_methodinfo(DocData::MethodDoc &p_signal, const MethodInfo &p_methodinfo, const String &p_desc);
|
||||
|
@ -196,6 +196,37 @@
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="structs" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="struct" maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="member" maxOccurs="unbounded" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute type="xs:string" name="name" use="required"/>
|
||||
<xs:attribute type="xs:string" name="type" use="required"/>
|
||||
<xs:attribute type="xs:string" name="default" use="required"/>
|
||||
<xs:attribute type="xs:string" name="enum" use="optional" />
|
||||
<xs:attribute type="xs:boolean" name="is_bitfield" use="optional" />
|
||||
<xs:attribute type="xs:boolean" name="is_deprecated" use="optional" />
|
||||
<xs:attribute type="xs:boolean" name="is_experimental" use="optional" />
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element type="xs:string" name="description" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute type="xs:string" name="name" use="required"/>
|
||||
<xs:attribute type="xs:boolean" name="is_deprecated" use="optional" />
|
||||
<xs:attribute type="xs:boolean" name="is_experimental" use="optional" />
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="annotations" minOccurs="0">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
|
@ -66,6 +66,7 @@ table_columns = [
|
||||
"description",
|
||||
"methods",
|
||||
"constants",
|
||||
"structs",
|
||||
"members",
|
||||
"theme_items",
|
||||
"signals",
|
||||
@ -78,6 +79,7 @@ table_column_names = [
|
||||
"Desc.",
|
||||
"Methods",
|
||||
"Constants",
|
||||
"Structs",
|
||||
"Members",
|
||||
"Theme Items",
|
||||
"Signals",
|
||||
@ -191,6 +193,7 @@ class ClassStatus:
|
||||
self.progresses: Dict[str, ClassStatusProgress] = {
|
||||
"methods": ClassStatusProgress(),
|
||||
"constants": ClassStatusProgress(),
|
||||
"structs": ClassStatusProgress(),
|
||||
"members": ClassStatusProgress(),
|
||||
"theme_items": ClassStatusProgress(),
|
||||
"signals": ClassStatusProgress(),
|
||||
@ -239,7 +242,7 @@ class ClassStatus:
|
||||
)
|
||||
items_progress = ClassStatusProgress()
|
||||
|
||||
for k in ["methods", "constants", "members", "theme_items", "signals", "constructors", "operators"]:
|
||||
for k in ["methods", "constants", "structs", "members", "theme_items", "signals", "constructors", "operators"]:
|
||||
items_progress += self.progresses[k]
|
||||
output[k] = self.progresses[k].to_configured_colored_string()
|
||||
|
||||
@ -284,7 +287,7 @@ class ClassStatus:
|
||||
descr = sub_tag.find("description")
|
||||
has_descr = (descr is not None) and (descr.text is not None) and len(descr.text.strip()) > 0
|
||||
status.progresses[tag.tag].increment(is_deprecated or is_experimental or has_descr)
|
||||
elif tag.tag in ["constants", "members", "theme_items"]:
|
||||
elif tag.tag in ["constants", "structs", "members", "theme_items"]:
|
||||
for sub_tag in list(tag):
|
||||
if sub_tag.text is not None:
|
||||
is_deprecated = "deprecated" in sub_tag.attrib
|
||||
@ -327,7 +330,7 @@ for arg in sys.argv[1:]:
|
||||
sys.exit(1)
|
||||
|
||||
if flags["i"]:
|
||||
for r in ["methods", "constants", "members", "signals", "theme_items"]:
|
||||
for r in ["methods", "constants", "structs", "members", "signals", "theme_items"]:
|
||||
index = table_columns.index(r)
|
||||
del table_column_names[index]
|
||||
del table_columns[index]
|
||||
|
@ -44,6 +44,7 @@ BASE_STRINGS = [
|
||||
"Theme Properties",
|
||||
"Signals",
|
||||
"Enumerations",
|
||||
"Structs",
|
||||
"Constants",
|
||||
"Annotations",
|
||||
"Property Descriptions",
|
||||
@ -69,6 +70,7 @@ BASE_STRINGS = [
|
||||
"There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
"There is currently no description for this signal. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
"There is currently no description for this enum. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
"There is currently no description for this struct. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
"There is currently no description for this constant. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
"There is currently no description for this annotation. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
"There is currently no description for this property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
|
||||
@ -342,6 +344,30 @@ class State:
|
||||
|
||||
enum_def.values[constant_name] = constant_def
|
||||
|
||||
structs = class_root.find("structs")
|
||||
if structs is not None:
|
||||
for struct in structs:
|
||||
assert struct.tag == "struct"
|
||||
|
||||
struct_name = struct.attrib["name"]
|
||||
if struct_name in class_def.structs:
|
||||
print_error(f'{class_name}.xml: Duplicate struct "{struct_name}".', self)
|
||||
continue
|
||||
struct_def = StructDef(struct_name)
|
||||
|
||||
for member in struct.findall("member"):
|
||||
member_name = member.attrib["name"]
|
||||
if member_name in class_def.structs:
|
||||
print_error(f'{class_name}.xml: Duplicate struct "{struct_name}".', self)
|
||||
continue
|
||||
member_type = TypeName(member.attrib["type"])
|
||||
member_default = member.attrib.get("default", None)
|
||||
if member_default is not None:
|
||||
member_default = f"``{member_default}``"
|
||||
struct_def.members[member_name] = ParameterDef(method_name, member_type, member_default)
|
||||
|
||||
class_def.structs[struct_name] = struct_def
|
||||
|
||||
annotations = class_root.find("annotations")
|
||||
if annotations is not None:
|
||||
for annotation in annotations:
|
||||
@ -577,6 +603,13 @@ class EnumDef(DefinitionBase):
|
||||
self.is_bitfield = bitfield
|
||||
|
||||
|
||||
class StructDef(DefinitionBase):
|
||||
def __init__(self, name: str) -> None:
|
||||
super().__init__("struct", name)
|
||||
|
||||
self.members: OrderedDict[str, ParameterDef] = OrderedDict()
|
||||
|
||||
|
||||
class ThemeItemDef(DefinitionBase):
|
||||
def __init__(
|
||||
self, name: str, type_name: TypeName, data_name: str, text: Optional[str], default_value: Optional[str]
|
||||
@ -598,6 +631,7 @@ class ClassDef(DefinitionBase):
|
||||
|
||||
self.constants: OrderedDict[str, ConstantDef] = OrderedDict()
|
||||
self.enums: OrderedDict[str, EnumDef] = OrderedDict()
|
||||
self.structs: OrderedDict[str, StructDef] = OrderedDict()
|
||||
self.properties: OrderedDict[str, PropertyDef] = OrderedDict()
|
||||
self.constructors: OrderedDict[str, List[MethodDef]] = OrderedDict()
|
||||
self.methods: OrderedDict[str, List[MethodDef]] = OrderedDict()
|
||||
@ -1219,6 +1253,32 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
|
||||
|
||||
f.write("\n\n")
|
||||
|
||||
# Struct descriptions
|
||||
if len(class_def.structs) > 0:
|
||||
f.write(make_separator(True))
|
||||
f.write(".. rst-class:: classref-descriptions-group\n\n")
|
||||
f.write(make_heading("Structs", "-"))
|
||||
|
||||
index = 0
|
||||
|
||||
for struct_def in class_def.structs.values():
|
||||
if index != 0:
|
||||
f.write(make_separator())
|
||||
|
||||
# Create struct signature and anchor point.
|
||||
|
||||
f.write(f".. _struct_{class_name}_{struct_def.name}:\n\n")
|
||||
f.write(".. rst-class:: classref-struct\n\n")
|
||||
f.write(f"struct **{struct_def.name}**:\n\n")
|
||||
|
||||
# Write struct members
|
||||
for member_name, member in struct_def.members.items():
|
||||
f.write(f".. _struct_{class_name}_{struct_def.name}_member_{member_name}:\n\n")
|
||||
f.write(".. rst-class:: classref-struct-member\n\n")
|
||||
f.write(f"{member.type_name.to_rst(state)} **{member_name}** = ``{member.default_value}``\n\n")
|
||||
|
||||
index += 1
|
||||
|
||||
# Annotation descriptions
|
||||
if len(class_def.annotations) > 0:
|
||||
f.write(make_separator(True))
|
||||
@ -1512,9 +1572,13 @@ def make_type(klass: str, state: State) -> str:
|
||||
def resolve_type(link_type: str) -> str:
|
||||
if link_type in state.classes:
|
||||
return f":ref:`{link_type}<class_{link_type}>`"
|
||||
else:
|
||||
print_error(f'{state.current_class}.xml: Unresolved type "{link_type}".', state)
|
||||
return f"``{link_type}``"
|
||||
|
||||
for class_name, class_def in state.classes.items(): # Check structs
|
||||
if link_type in class_def.structs:
|
||||
return f":ref:`{link_type}<struct_{class_name}_link_type>`"
|
||||
|
||||
print_error(f'{state.current_class}.xml: Unresolved type "{link_type}".', state)
|
||||
return f"``{link_type}``"
|
||||
|
||||
if klass.endswith("[]"): # Typed array, strip [] to link to contained type.
|
||||
return f":ref:`Array<class_Array>`\\[{resolve_type(klass[:-len('[]')])}\\]"
|
||||
@ -1558,6 +1622,27 @@ def make_enum(t: str, is_bitfield: bool, state: State) -> str:
|
||||
return t
|
||||
|
||||
|
||||
def make_struct(t: str, state: State) -> str:
|
||||
p = t.find(".")
|
||||
if p >= 0:
|
||||
c = t[0:p]
|
||||
e = t[p + 1 :]
|
||||
# Variant structs live in GlobalScope but still use periods.
|
||||
if c == "Variant":
|
||||
c = "@GlobalScope"
|
||||
e = "Variant." + e
|
||||
else:
|
||||
c = state.current_class
|
||||
e = t
|
||||
if c in state.classes and e not in state.classes[c].structs:
|
||||
c = "@GlobalScope"
|
||||
|
||||
if c in state.classes and e in state.classes[c].structs:
|
||||
return f":ref:`{e}<struct_{c}_{e}>`"
|
||||
|
||||
return t
|
||||
|
||||
|
||||
def make_method_signature(
|
||||
class_def: ClassDef, definition: Union[AnnotationDef, MethodDef, SignalDef], ref_type: str, state: State
|
||||
) -> Tuple[str, str]:
|
||||
@ -1797,6 +1882,7 @@ RESERVED_CROSSLINK_TAGS = [
|
||||
"signal",
|
||||
"constant",
|
||||
"enum",
|
||||
"struct",
|
||||
"annotation",
|
||||
"theme_item",
|
||||
"param",
|
||||
@ -2113,6 +2199,12 @@ def format_text_block(
|
||||
state,
|
||||
)
|
||||
|
||||
elif target_name in class_def.structs:
|
||||
print_warning(
|
||||
f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} struct in {context_name}. {code_warning_if_intended_string}',
|
||||
state,
|
||||
)
|
||||
|
||||
else:
|
||||
for enum in class_def.enums.values():
|
||||
if target_name in enum.values:
|
||||
@ -2151,6 +2243,7 @@ def format_text_block(
|
||||
or tag_state.name == "member"
|
||||
or tag_state.name == "signal"
|
||||
or tag_state.name == "annotation"
|
||||
or tag_state.name == "struct"
|
||||
or tag_state.name == "theme_item"
|
||||
or tag_state.name == "constant"
|
||||
):
|
||||
@ -2211,6 +2304,12 @@ def format_text_block(
|
||||
state,
|
||||
)
|
||||
|
||||
elif tag_state.name == "struct" and target_name not in class_def.structs:
|
||||
print_error(
|
||||
f'{state.current_class}.xml: Unresolved struct reference "{link_target}" in {context_name}.',
|
||||
state,
|
||||
)
|
||||
|
||||
elif tag_state.name == "theme_item":
|
||||
if target_name not in class_def.theme_items:
|
||||
print_error(
|
||||
@ -2268,6 +2367,11 @@ def format_text_block(
|
||||
escape_pre = True
|
||||
escape_post = True
|
||||
|
||||
elif tag_state.name == "struct":
|
||||
tag_text = make_struct(link_target, state)
|
||||
escape_pre = True
|
||||
escape_post = True
|
||||
|
||||
elif tag_state.name == "param":
|
||||
valid_param_context = isinstance(context, (MethodDef, SignalDef, AnnotationDef))
|
||||
if not valid_param_context:
|
||||
|
@ -258,6 +258,32 @@ static void merge_properties(Vector<DocData::PropertyDoc> &p_to, const Vector<Do
|
||||
}
|
||||
}
|
||||
|
||||
static void merge_structs(Vector<DocData::StructDoc> &p_to, const Vector<DocData::StructDoc> &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::StructDoc *to_ptrw = p_to.ptrw();
|
||||
int64_t to_size = p_to.size();
|
||||
|
||||
SearchArray<DocData::StructDoc> search_array;
|
||||
|
||||
for (const DocData::StructDoc &from : p_from) {
|
||||
int64_t found = search_array.bisect(to_ptrw, to_size, from, true);
|
||||
|
||||
if (found >= to_size) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DocData::StructDoc &to = to_ptrw[found];
|
||||
|
||||
// Check found entry on name and data type.
|
||||
if (to.name == from.name) {
|
||||
to.description = from.description;
|
||||
to.is_deprecated = from.is_deprecated;
|
||||
to.is_experimental = from.is_experimental;
|
||||
merge_properties(to.properties, from.properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
@ -345,6 +371,8 @@ void DocTools::merge_from(const DocTools &p_data) {
|
||||
|
||||
merge_properties(c.properties, cf.properties);
|
||||
|
||||
merge_structs(c.structs, cf.structs);
|
||||
|
||||
merge_theme_properties(c.theme_properties, cf.theme_properties);
|
||||
|
||||
merge_operators(c.operators, cf.operators);
|
||||
@ -679,6 +707,15 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
|
||||
c.constants.push_back(constant);
|
||||
}
|
||||
|
||||
List<StructInfo> struct_list;
|
||||
ClassDB::get_struct_list(name, &struct_list, true);
|
||||
|
||||
for (const StructInfo &E : struct_list) {
|
||||
DocData::StructDoc struct_doc;
|
||||
DocData::struct_doc_from_structinfo(struct_doc, E);
|
||||
c.structs.push_back(struct_doc);
|
||||
}
|
||||
|
||||
// Theme items.
|
||||
{
|
||||
List<ThemeDB::ThemeItemBind> theme_items;
|
||||
@ -1114,85 +1151,83 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> &
|
||||
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);
|
||||
method.name = parser->get_named_attribute_value("name");
|
||||
if (parser->has_attribute("qualifiers")) {
|
||||
method.qualifiers = parser->get_named_attribute_value("qualifiers");
|
||||
}
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == section) {
|
||||
break;
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
ERR_FAIL_COND_V_MSG(parser->get_node_name() != element, ERR_FILE_CORRUPT, "Invalid tag in doc file: " + parser->get_node_name() + ", expected " + element + ".");
|
||||
DocData::MethodDoc method;
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
method.name = parser->get_named_attribute_value("name");
|
||||
if (parser->has_attribute("qualifiers")) {
|
||||
method.qualifiers = parser->get_named_attribute_value("qualifiers");
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
method.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
method.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
method.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
method.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
#endif
|
||||
if (parser->has_attribute("deprecated")) {
|
||||
method.is_deprecated = true;
|
||||
method.deprecated_message = parser->get_named_attribute_value("deprecated");
|
||||
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");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
method.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == element) {
|
||||
break;
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
String name = parser->get_node_name();
|
||||
if (name == "return") {
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
method.return_type = parser->get_named_attribute_value("type");
|
||||
if (parser->has_attribute("enum")) {
|
||||
method.return_enum = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
method.return_is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
if (parser->has_attribute("experimental")) {
|
||||
method.is_experimental = true;
|
||||
method.experimental_message = parser->get_named_attribute_value("experimental");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
method.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
|
||||
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);
|
||||
method.return_type = parser->get_named_attribute_value("type");
|
||||
if (parser->has_attribute("enum")) {
|
||||
method.return_enum = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
method.return_is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
} else if (name == "returns_error") {
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("number"), ERR_FILE_CORRUPT);
|
||||
method.errors_returned.push_back(parser->get_named_attribute_value("number").to_int());
|
||||
} else if (name == "param") {
|
||||
DocData::ArgumentDoc argument;
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
argument.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
argument.type = parser->get_named_attribute_value("type");
|
||||
if (parser->has_attribute("enum")) {
|
||||
argument.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
argument.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
|
||||
method.arguments.push_back(argument);
|
||||
|
||||
} else if (name == "description") {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
method.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == element) {
|
||||
break;
|
||||
} else if (name == "returns_error") {
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("number"), ERR_FILE_CORRUPT);
|
||||
method.errors_returned.push_back(parser->get_named_attribute_value("number").to_int());
|
||||
} else if (name == "param") {
|
||||
DocData::ArgumentDoc argument;
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
argument.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
argument.type = parser->get_named_attribute_value("type");
|
||||
if (parser->has_attribute("enum")) {
|
||||
argument.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
argument.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
|
||||
methods.push_back(method);
|
||||
method.arguments.push_back(argument);
|
||||
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + parser->get_node_name() + ", expected " + element + ".");
|
||||
} else if (name == "description") {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
method.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == section) {
|
||||
break;
|
||||
}
|
||||
|
||||
methods.push_back(method);
|
||||
}
|
||||
|
||||
return OK;
|
||||
@ -1269,7 +1304,7 @@ Error DocTools::_load(Ref<XMLParser> parser) {
|
||||
ERR_FAIL_COND_V(parser->get_node_name() != "class", ERR_FILE_CORRUPT);
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
String name = parser->get_named_attribute_value("name");
|
||||
const String name = parser->get_named_attribute_value("name");
|
||||
class_list[name] = DocData::ClassDoc();
|
||||
DocData::ClassDoc &c = class_list[name];
|
||||
|
||||
@ -1302,216 +1337,286 @@ Error DocTools::_load(Ref<XMLParser> parser) {
|
||||
}
|
||||
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
String name2 = parser->get_node_name();
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "class") {
|
||||
break; // End of <class>.
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
const String tag = parser->get_node_name();
|
||||
|
||||
if (name2 == "brief_description") {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
c.brief_description = parser->get_node_data();
|
||||
}
|
||||
|
||||
} else if (name2 == "description") {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
c.description = parser->get_node_data();
|
||||
}
|
||||
} else if (name2 == "tutorials") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
String name3 = parser->get_node_name();
|
||||
|
||||
if (name3 == "link") {
|
||||
DocData::TutorialDoc tutorial;
|
||||
if (parser->has_attribute("title")) {
|
||||
tutorial.title = parser->get_named_attribute_value("title");
|
||||
}
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
tutorial.link = parser->get_node_data().strip_edges();
|
||||
c.tutorials.push_back(tutorial);
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + name3 + ".");
|
||||
}
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "tutorials") {
|
||||
break; // End of <tutorials>.
|
||||
}
|
||||
}
|
||||
} else if (name2 == "constructors") {
|
||||
Error err2 = _parse_methods(parser, c.constructors);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (name2 == "methods") {
|
||||
Error err2 = _parse_methods(parser, c.methods);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (name2 == "operators") {
|
||||
Error err2 = _parse_methods(parser, c.operators);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (name2 == "signals") {
|
||||
Error err2 = _parse_methods(parser, c.signals);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (name2 == "annotations") {
|
||||
Error err2 = _parse_methods(parser, c.annotations);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (name2 == "members") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
String name3 = parser->get_node_name();
|
||||
|
||||
if (name3 == "member") {
|
||||
DocData::PropertyDoc prop2;
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
prop2.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
prop2.type = parser->get_named_attribute_value("type");
|
||||
if (parser->has_attribute("setter")) {
|
||||
prop2.setter = parser->get_named_attribute_value("setter");
|
||||
}
|
||||
if (parser->has_attribute("getter")) {
|
||||
prop2.getter = parser->get_named_attribute_value("getter");
|
||||
}
|
||||
if (parser->has_attribute("enum")) {
|
||||
prop2.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
prop2.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
prop2.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
prop2.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
#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");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
prop2.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
if (!parser->is_empty()) {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
prop2.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
c.properties.push_back(prop2);
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + name3 + ".");
|
||||
}
|
||||
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "members") {
|
||||
break; // End of <members>.
|
||||
}
|
||||
}
|
||||
|
||||
} else if (name2 == "theme_items") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
String name3 = parser->get_node_name();
|
||||
|
||||
if (name3 == "theme_item") {
|
||||
DocData::ThemeItemDoc prop2;
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
prop2.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
prop2.type = parser->get_named_attribute_value("type");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("data_type"), ERR_FILE_CORRUPT);
|
||||
prop2.data_type = parser->get_named_attribute_value("data_type");
|
||||
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");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
prop2.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
if (!parser->is_empty()) {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
prop2.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
c.theme_properties.push_back(prop2);
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + name3 + ".");
|
||||
}
|
||||
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "theme_items") {
|
||||
break; // End of <theme_items>.
|
||||
}
|
||||
}
|
||||
|
||||
} else if (name2 == "constants") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT) {
|
||||
String name3 = parser->get_node_name();
|
||||
|
||||
if (name3 == "constant") {
|
||||
DocData::ConstantDoc constant2;
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
constant2.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("value"), ERR_FILE_CORRUPT);
|
||||
constant2.value = parser->get_named_attribute_value("value");
|
||||
constant2.is_value_valid = true;
|
||||
if (parser->has_attribute("enum")) {
|
||||
constant2.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
constant2.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
constant2.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
constant2.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
#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");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
constant2.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
if (!parser->is_empty()) {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
constant2.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
c.constants.push_back(constant2);
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + name3 + ".");
|
||||
}
|
||||
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "constants") {
|
||||
break; // End of <constants>.
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + name2 + ".");
|
||||
if (tag == "brief_description") {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
c.brief_description = parser->get_node_data();
|
||||
}
|
||||
|
||||
} else if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "class") {
|
||||
break; // End of <class>.
|
||||
} else if (tag == "description") {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
c.description = parser->get_node_data();
|
||||
}
|
||||
} else if (tag == "tutorials") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "tutorials") {
|
||||
break; // End of <tutorials>.
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
const String link_tag = parser->get_node_name();
|
||||
ERR_FAIL_COND_V_MSG(link_tag != "link", ERR_FILE_CORRUPT, "Invalid tag in doc file: " + link_tag + ", expected link.");
|
||||
DocData::TutorialDoc tutorial;
|
||||
if (parser->has_attribute("title")) {
|
||||
tutorial.title = parser->get_named_attribute_value("title");
|
||||
}
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
tutorial.link = parser->get_node_data().strip_edges();
|
||||
c.tutorials.push_back(tutorial);
|
||||
}
|
||||
}
|
||||
} else if (tag == "constructors") {
|
||||
const Error err2 = _parse_methods(parser, c.constructors);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (tag == "methods") {
|
||||
const Error err2 = _parse_methods(parser, c.methods);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (tag == "operators") {
|
||||
const Error err2 = _parse_methods(parser, c.operators);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (tag == "signals") {
|
||||
const Error err2 = _parse_methods(parser, c.signals);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (tag == "annotations") {
|
||||
const Error err2 = _parse_methods(parser, c.annotations);
|
||||
ERR_FAIL_COND_V(err2, err2);
|
||||
} else if (tag == "members") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "members") {
|
||||
break; // End of <members>.
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
String member_tag = parser->get_node_name();
|
||||
ERR_FAIL_COND_V_MSG(member_tag != "member", ERR_FILE_CORRUPT, "Invalid tag in doc file: " + member_tag + ", expected member.");
|
||||
DocData::PropertyDoc property_doc;
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
property_doc.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
property_doc.type = parser->get_named_attribute_value("type");
|
||||
if (parser->has_attribute("setter")) {
|
||||
property_doc.setter = parser->get_named_attribute_value("setter");
|
||||
}
|
||||
if (parser->has_attribute("getter")) {
|
||||
property_doc.getter = parser->get_named_attribute_value("getter");
|
||||
}
|
||||
if (parser->has_attribute("enum")) {
|
||||
property_doc.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
property_doc.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
property_doc.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
property_doc.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
#endif
|
||||
if (parser->has_attribute("deprecated")) {
|
||||
property_doc.is_deprecated = true;
|
||||
property_doc.deprecated_message = parser->get_named_attribute_value("deprecated");
|
||||
}
|
||||
if (parser->has_attribute("experimental")) {
|
||||
property_doc.is_experimental = true;
|
||||
property_doc.experimental_message = parser->get_named_attribute_value("experimental");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
property_doc.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
if (!parser->is_empty()) {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
property_doc.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
c.properties.push_back(property_doc);
|
||||
}
|
||||
} else if (tag == "structs") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "structs") {
|
||||
break; // End of <structs>.
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
const String struct_tag = parser->get_node_name();
|
||||
ERR_FAIL_COND_V_MSG(struct_tag != "struct", ERR_FILE_CORRUPT, "Invalid tag in doc file: " + struct_tag + ", expected struct.");
|
||||
|
||||
DocData::StructDoc struct_doc;
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
struct_doc.name = parser->get_named_attribute_value("name");
|
||||
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
struct_doc.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
struct_doc.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "struct") {
|
||||
break; // End of <struct>
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
const String member_tag = parser->get_node_name();
|
||||
ERR_FAIL_COND_V_MSG(member_tag != "member", ERR_FILE_CORRUPT, "Invalid tag in doc file: " + member_tag + ", expected member.");
|
||||
DocData::PropertyDoc member_doc;
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
member_doc.name = parser->get_named_attribute_value("name");
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
member_doc.type = parser->get_named_attribute_value("type");
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("default"), ERR_FILE_CORRUPT);
|
||||
member_doc.default_value = parser->get_named_attribute_value("default");
|
||||
|
||||
if (parser->has_attribute("enum")) {
|
||||
member_doc.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
member_doc.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
member_doc.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
member_doc.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
|
||||
// TODO: figure out description
|
||||
|
||||
// if (!parser->is_empty()) {
|
||||
// parser->read();
|
||||
// if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
// member_doc.description = parser->get_node_data().strip_edges();
|
||||
// }
|
||||
// }
|
||||
|
||||
struct_doc.properties.push_back(member_doc);
|
||||
}
|
||||
|
||||
// if (!parser->is_empty()) {
|
||||
// parser->read();
|
||||
// if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
// struct_doc.description = parser->get_node_data().strip_edges();
|
||||
// }
|
||||
// }
|
||||
|
||||
c.structs.push_back(struct_doc);
|
||||
}
|
||||
} else if (tag == "theme_items") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "theme_items") {
|
||||
break; // End of <theme_items>.
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
const String theme_item_tag = parser->get_node_name();
|
||||
ERR_FAIL_COND_V_MSG(theme_item_tag != "theme_item", ERR_FILE_CORRUPT, "Invalid tag in doc file: " + theme_item_tag + ", expected theme_item.");
|
||||
|
||||
DocData::ThemeItemDoc theme_item_doc;
|
||||
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
theme_item_doc.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("type"), ERR_FILE_CORRUPT);
|
||||
theme_item_doc.type = parser->get_named_attribute_value("type");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("data_type"), ERR_FILE_CORRUPT);
|
||||
theme_item_doc.data_type = parser->get_named_attribute_value("data_type");
|
||||
if (parser->has_attribute("deprecated")) {
|
||||
theme_item_doc.is_deprecated = true;
|
||||
theme_item_doc.deprecated_message = parser->get_named_attribute_value("deprecated");
|
||||
}
|
||||
if (parser->has_attribute("experimental")) {
|
||||
theme_item_doc.is_experimental = true;
|
||||
theme_item_doc.experimental_message = parser->get_named_attribute_value("experimental");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
theme_item_doc.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
if (!parser->is_empty()) {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
theme_item_doc.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
c.theme_properties.push_back(theme_item_doc);
|
||||
}
|
||||
} else if (tag == "constants") {
|
||||
while (parser->read() == OK) {
|
||||
if (parser->get_node_type() == XMLParser::NODE_ELEMENT_END && parser->get_node_name() == "constants") {
|
||||
break; // End of <constants>.
|
||||
}
|
||||
if (parser->get_node_type() != XMLParser::NODE_ELEMENT) {
|
||||
continue;
|
||||
}
|
||||
const String constant_tag = parser->get_node_name();
|
||||
|
||||
ERR_FAIL_COND_V_MSG(constant_tag != "constant", ERR_FILE_CORRUPT, "Invalid tag in doc file: " + constant_tag + ", expected constant.");
|
||||
|
||||
DocData::ConstantDoc constant_doc;
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
|
||||
constant_doc.name = parser->get_named_attribute_value("name");
|
||||
ERR_FAIL_COND_V(!parser->has_attribute("value"), ERR_FILE_CORRUPT);
|
||||
constant_doc.value = parser->get_named_attribute_value("value");
|
||||
constant_doc.is_value_valid = true;
|
||||
if (parser->has_attribute("enum")) {
|
||||
constant_doc.enumeration = parser->get_named_attribute_value("enum");
|
||||
if (parser->has_attribute("is_bitfield")) {
|
||||
constant_doc.is_bitfield = parser->get_named_attribute_value("is_bitfield").to_lower() == "true";
|
||||
}
|
||||
}
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
if (parser->has_attribute("is_deprecated")) {
|
||||
constant_doc.is_deprecated = parser->get_named_attribute_value("is_deprecated").to_lower() == "true";
|
||||
}
|
||||
if (parser->has_attribute("is_experimental")) {
|
||||
constant_doc.is_experimental = parser->get_named_attribute_value("is_experimental").to_lower() == "true";
|
||||
}
|
||||
#endif
|
||||
if (parser->has_attribute("deprecated")) {
|
||||
constant_doc.is_deprecated = true;
|
||||
constant_doc.deprecated_message = parser->get_named_attribute_value("deprecated");
|
||||
}
|
||||
if (parser->has_attribute("experimental")) {
|
||||
constant_doc.is_experimental = true;
|
||||
constant_doc.experimental_message = parser->get_named_attribute_value("experimental");
|
||||
}
|
||||
if (parser->has_attribute("keywords")) {
|
||||
constant_doc.keywords = parser->get_named_attribute_value("keywords");
|
||||
}
|
||||
if (!parser->is_empty()) {
|
||||
parser->read();
|
||||
if (parser->get_node_type() == XMLParser::NODE_TEXT) {
|
||||
constant_doc.description = parser->get_node_data();
|
||||
}
|
||||
}
|
||||
c.constants.push_back(constant_doc);
|
||||
}
|
||||
} else {
|
||||
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Invalid tag in doc file: " + tag + ".");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1743,6 +1848,53 @@ Error DocTools::save_classes(const String &p_default_path, const HashMap<String,
|
||||
|
||||
_write_method_doc(f, "annotation", c.annotations);
|
||||
|
||||
if (!c.structs.is_empty()) {
|
||||
_write_string(f, 1, "<structs>");
|
||||
for (int i = 0; i < c.structs.size(); i++) {
|
||||
const DocData::StructDoc &s = c.structs[i];
|
||||
|
||||
String struct_header = "<struct name=\"" + s.name.xml_escape(true) + "\"";
|
||||
if (s.is_deprecated) {
|
||||
struct_header += " is_deprecated=\"true\"";
|
||||
}
|
||||
if (s.is_experimental) {
|
||||
struct_header += " is_experimental=\"true\"";
|
||||
}
|
||||
struct_header += ">";
|
||||
_write_string(f, 2, struct_header);
|
||||
|
||||
if (!s.description.is_empty()) {
|
||||
_write_string(f, 3, "<description>" + s.description.xml_escape() + "</description>");
|
||||
}
|
||||
|
||||
for (int j = 0; j < s.properties.size(); j++) {
|
||||
const DocData::PropertyDoc &m = s.properties[j];
|
||||
String member_line = "<member name=\"" + m.name.xml_escape(true) + "\" type=\"" + m.type.xml_escape(true) + "\" default=\"" + m.default_value.xml_escape(true) + "\"";
|
||||
|
||||
if (!m.description.is_empty()) {
|
||||
member_line += " description=\"" + m.description.xml_escape() + "\"";
|
||||
}
|
||||
if (!m.enumeration.is_empty()) {
|
||||
member_line += " enum=\"" + m.enumeration.xml_escape() + "\"";
|
||||
}
|
||||
if (m.is_bitfield) {
|
||||
member_line += " is_bitfield=\"true\"";
|
||||
}
|
||||
if (m.is_deprecated) {
|
||||
member_line += " is_deprecated=\"true\"";
|
||||
}
|
||||
if (m.is_experimental) {
|
||||
member_line += " is_experimental=\"true\"";
|
||||
}
|
||||
member_line += "/>";
|
||||
_write_string(f, 3, member_line);
|
||||
}
|
||||
|
||||
_write_string(f, 2, "</struct>");
|
||||
}
|
||||
_write_string(f, 1, "</structs>");
|
||||
}
|
||||
|
||||
if (!c.theme_properties.is_empty()) {
|
||||
_write_string(f, 1, "<theme_items>");
|
||||
for (int i = 0; i < c.theme_properties.size(); i++) {
|
||||
|
@ -282,6 +282,9 @@ void EditorHelp::_class_desc_select(const String &p_select) {
|
||||
} else if (tag == "theme_item") {
|
||||
topic = "class_theme_item";
|
||||
table = &theme_property_line;
|
||||
} else if (tag == "struct") {
|
||||
topic = "struct";
|
||||
table = &struct_line;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -1640,6 +1643,92 @@ void EditorHelp::_update_doc() {
|
||||
}
|
||||
}
|
||||
|
||||
// Structs
|
||||
if (!cd.structs.is_empty()) {
|
||||
class_desc->add_newline();
|
||||
class_desc->add_newline();
|
||||
|
||||
section_line.push_back(Pair<String, int>(TTR("Structs"), class_desc->get_paragraph_count() - 2));
|
||||
_push_title_font();
|
||||
class_desc->add_text(TTR("Structs"));
|
||||
_pop_title_font();
|
||||
|
||||
for (const DocData::StructDoc &struct_item : cd.structs) {
|
||||
class_desc->add_newline();
|
||||
class_desc->add_newline();
|
||||
|
||||
struct_line[struct_item.name] = class_desc->get_paragraph_count() - 2; // Gets overridden if description.
|
||||
|
||||
class_desc->push_indent(1);
|
||||
_push_code_font();
|
||||
|
||||
// Struct name.
|
||||
class_desc->push_color(theme_cache.headline_color);
|
||||
class_desc->add_text(struct_item.name);
|
||||
class_desc->pop(); // color
|
||||
|
||||
_pop_code_font();
|
||||
|
||||
// Struct description.
|
||||
class_desc->push_indent(1);
|
||||
_push_normal_font();
|
||||
class_desc->push_color(theme_cache.comment_color);
|
||||
|
||||
const String descr = DTR(struct_item.description).strip_edges();
|
||||
if (!descr.is_empty()) {
|
||||
_add_text(descr);
|
||||
} else {
|
||||
class_desc->add_image(get_editor_theme_icon(SNAME("Error")));
|
||||
class_desc->add_text(" ");
|
||||
class_desc->push_color(theme_cache.comment_color);
|
||||
if (cd.is_script_doc) {
|
||||
class_desc->add_text(TTR("There is currently no description for this struct."));
|
||||
} else {
|
||||
class_desc->append_text(TTR("There is currently no description for this struct. Please help us by [color=$color][url=$url]contributing one[/url][/color]!").replace("$url", CONTRIBUTE_URL).replace("$color", link_color_text));
|
||||
}
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
class_desc->pop(); // color
|
||||
_pop_normal_font();
|
||||
class_desc->pop(); // indent
|
||||
|
||||
// Struct members.
|
||||
for (const DocData::PropertyDoc &member : struct_item.properties) {
|
||||
class_desc->add_newline();
|
||||
class_desc->push_indent(1);
|
||||
_push_code_font();
|
||||
|
||||
// Member type and name.
|
||||
_add_bulletpoint();
|
||||
_add_type(member.type);
|
||||
class_desc->add_text(" " + member.name);
|
||||
|
||||
// Member default value.
|
||||
if (!member.default_value.is_empty()) {
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text(" [" + TTR("default:") + " ");
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.value_color);
|
||||
class_desc->add_text(_fix_constant(member.default_value));
|
||||
class_desc->pop(); // color
|
||||
|
||||
class_desc->push_color(theme_cache.symbol_color);
|
||||
class_desc->add_text("]");
|
||||
class_desc->pop(); // color
|
||||
}
|
||||
|
||||
_pop_code_font();
|
||||
class_desc->pop(); // indent
|
||||
}
|
||||
|
||||
class_desc->pop(); // indent for struct members
|
||||
class_desc->add_newline();
|
||||
}
|
||||
class_desc->add_newline();
|
||||
}
|
||||
|
||||
// Constants and enums
|
||||
if (!cd.constants.is_empty()) {
|
||||
HashMap<String, Vector<DocData::ConstantDoc>> enums;
|
||||
@ -2345,6 +2434,10 @@ void EditorHelp::_help_callback(const String &p_topic) {
|
||||
if (enum_line.has(name)) {
|
||||
line = enum_line[name];
|
||||
}
|
||||
} else if (what == "class_struct") {
|
||||
if (struct_line.has(name)) {
|
||||
line = struct_line[name];
|
||||
}
|
||||
} else if (what == "class_theme_item") {
|
||||
if (theme_property_line.has(name)) {
|
||||
line = theme_property_line[name];
|
||||
|
@ -110,6 +110,8 @@ class EditorHelp : public VBoxContainer {
|
||||
HashMap<String, int> annotation_line;
|
||||
HashMap<String, int> enum_line;
|
||||
HashMap<String, HashMap<String, int>> enum_values_line;
|
||||
HashMap<String, int> struct_line;
|
||||
HashMap<String, HashMap<String, int>> struct_members_line;
|
||||
int description_line = 0;
|
||||
|
||||
RichTextLabel *class_desc = nullptr;
|
||||
|
Loading…
Reference in New Issue
Block a user