diff options
Diffstat (limited to 'doc/tools/make_rst.py')
-rwxr-xr-x | doc/tools/make_rst.py | 1108 |
1 files changed, 630 insertions, 478 deletions
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index 4435d52527..761a7f8f4a 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -4,17 +4,16 @@ import argparse import os -import platform import re import sys import xml.etree.ElementTree as ET from collections import OrderedDict -from typing import List, Dict, TextIO, Tuple, Optional, Any, Union +from typing import Any, Dict, List, Optional, TextIO, Tuple, Union # Import hardcoded version information from version.py root_directory = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../") sys.path.append(root_directory) # Include the root directory -import version +import version # noqa: E402 # $DOCS_URL/path/to/page.html(#fragment-tag) GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") @@ -66,6 +65,7 @@ BASE_STRINGS = [ "This method doesn't need an instance to be called, so it can be called directly using the class name.", "This method describes a valid operator to use with this type as left-hand operand.", "This value is an integer composed as a bitmask of the following flags.", + "No return value.", "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>`!", @@ -77,6 +77,16 @@ BASE_STRINGS = [ "There is currently no description for this operator. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!", "There is currently no description for this theme property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!", "There are notable differences when using this API with C#. See :ref:`doc_c_sharp_differences` for more information.", + "Deprecated:", + "Experimental:", + "This signal may be changed or removed in future versions.", + "This constant may be changed or removed in future versions.", + "This property may be changed or removed in future versions.", + "This constructor may be changed or removed in future versions.", + "This method may be changed or removed in future versions.", + "This operator may be changed or removed in future versions.", + "This theme property may be changed or removed in future versions.", + "[b]Note:[/b] The returned array is [i]copied[/i] and any changes to it will not update the original property value. See [%s] for more details.", ] strings_l10n: Dict[str, str] = {} @@ -132,9 +142,23 @@ CLASSES_WITH_CSHARP_DIFFERENCES: List[str] = [ "PackedStringArray", "PackedVector2Array", "PackedVector3Array", + "PackedVector4Array", "Variant", ] +PACKED_ARRAY_TYPES: List[str] = [ + "PackedByteArray", + "PackedColorArray", + "PackedFloat32Array", + "Packedfloat64Array", + "PackedInt32Array", + "PackedInt64Array", + "PackedStringArray", + "PackedVector2Array", + "PackedVector3Array", + "PackedVector4Array", +] + class State: def __init__(self) -> None: @@ -158,6 +182,9 @@ class State: if inherits is not None: class_def.inherits = inherits + class_def.deprecated = class_root.get("deprecated") + class_def.experimental = class_root.get("experimental") + brief_desc = class_root.find("brief_description") if brief_desc is not None and brief_desc.text: class_def.brief_description = brief_desc.text @@ -166,6 +193,10 @@ class State: if desc is not None and desc.text: class_def.description = desc.text + keywords = class_root.get("keywords") + if keywords is not None: + class_def.keywords = keywords + properties = class_root.find("members") if properties is not None: for property in properties: @@ -187,6 +218,8 @@ class State: property_def = PropertyDef( property_name, type_name, setter, getter, property.text, default_value, overrides ) + property_def.deprecated = property.get("deprecated") + property_def.experimental = property.get("experimental") class_def.properties[property_name] = property_def constructors = class_root.find("constructors") @@ -212,6 +245,8 @@ class State: method_def = MethodDef(method_name, return_type, params, method_desc, qualifiers) method_def.definition_name = "constructor" + method_def.deprecated = constructor.get("deprecated") + method_def.experimental = constructor.get("experimental") if method_name not in class_def.constructors: class_def.constructors[method_name] = [] @@ -240,6 +275,8 @@ class State: method_desc = desc_element.text method_def = MethodDef(method_name, return_type, params, method_desc, qualifiers) + method_def.deprecated = method.get("deprecated") + method_def.experimental = method.get("experimental") if method_name not in class_def.methods: class_def.methods[method_name] = [] @@ -269,6 +306,8 @@ class State: method_def = MethodDef(method_name, return_type, params, method_desc, qualifiers) method_def.definition_name = "operator" + method_def.deprecated = operator.get("deprecated") + method_def.experimental = operator.get("experimental") if method_name not in class_def.operators: class_def.operators[method_name] = [] @@ -284,6 +323,8 @@ class State: enum = constant.get("enum") is_bitfield = constant.get("is_bitfield") == "true" constant_def = ConstantDef(constant_name, value, constant.text, is_bitfield) + constant_def.deprecated = constant.get("deprecated") + constant_def.experimental = constant.get("experimental") if enum is None: if constant_name in class_def.constants: print_error(f'{class_name}.xml: Duplicate constant "{constant_name}".', self) @@ -341,6 +382,8 @@ class State: signal_desc = desc_element.text signal_def = SignalDef(signal_name, params, signal_desc) + signal_def.deprecated = signal.get("deprecated") + signal_def.experimental = signal.get("experimental") class_def.signals[signal_name] = signal_def theme_items = class_root.find("theme_items") @@ -353,7 +396,7 @@ class State: theme_item_id = "{}_{}".format(theme_item_data_name, theme_item_name) if theme_item_id in class_def.theme_items: print_error( - f'{class_name}.xml: Duplicate theme item "{theme_item_name}" of type "{theme_item_data_name}".', + f'{class_name}.xml: Duplicate theme property "{theme_item_name}" of type "{theme_item_data_name}".', self, ) continue @@ -408,7 +451,7 @@ class State: class TagState: - def __init__(self, raw: str, name: str, arguments: List[str], closing: bool) -> None: + def __init__(self, raw: str, name: str, arguments: str, closing: bool) -> None: self.raw = raw self.name = name @@ -426,7 +469,7 @@ class TypeName: if self.enum is not None: return make_enum(self.enum, self.is_bitfield, state) elif self.type_name == "void": - return "void" + return "|void|" else: return make_type(self.type_name, state) @@ -443,6 +486,8 @@ class DefinitionBase: ) -> None: self.definition_name = definition_name self.name = name + self.deprecated: Optional[str] = None + self.experimental: Optional[str] = None class PropertyDef(DefinitionBase): @@ -536,7 +581,7 @@ class ThemeItemDef(DefinitionBase): def __init__( self, name: str, type_name: TypeName, data_name: str, text: Optional[str], default_value: Optional[str] ) -> None: - super().__init__("theme item", name) + super().__init__("theme property", name) self.type_name = type_name self.data_name = data_name @@ -564,6 +609,7 @@ class ClassDef(DefinitionBase): self.brief_description: Optional[str] = None self.description: Optional[str] = None self.tutorials: List[Tuple[str, str]] = [] + self.keywords: Optional[str] = None # Used to match the class with XML source for output filtering purposes. self.filepath: str = "" @@ -630,17 +676,6 @@ class ScriptLanguageParityCheck: # Entry point for the RST generator. def main() -> None: - # Enable ANSI escape code support on Windows 10 and later (for colored console output). - # <https://bugs.python.org/issue29059> - if platform.system().lower() == "windows": - from ctypes import windll, c_int, byref # type: ignore - - stdout_handle = windll.kernel32.GetStdHandle(c_int(-11)) - mode = c_int(0) - windll.kernel32.GetConsoleMode(c_int(stdout_handle), byref(mode)) - mode = c_int(mode.value | 4) - windll.kernel32.SetConsoleMode(c_int(stdout_handle), mode) - parser = argparse.ArgumentParser() parser.add_argument("path", nargs="+", help="A path to an XML file or a directory containing XML files to parse.") parser.add_argument("--filter", default="", help="The filepath pattern for XML files to filter.") @@ -664,7 +699,25 @@ def main() -> None: ) args = parser.parse_args() - should_color = args.color or (hasattr(sys.stdout, "isatty") and sys.stdout.isatty()) + should_color = bool(args.color or sys.stdout.isatty() or os.environ.get("CI")) + + # Enable ANSI escape code support on Windows 10 and later (for colored console output). + # <https://github.com/python/cpython/issues/73245> + if should_color and sys.stdout.isatty() and sys.platform == "win32": + try: + from ctypes import WinError, byref, windll # type: ignore + from ctypes.wintypes import DWORD # type: ignore + + stdout_handle = windll.kernel32.GetStdHandle(DWORD(-11)) + mode = DWORD(0) + if not windll.kernel32.GetConsoleMode(stdout_handle, byref(mode)): + raise WinError() + mode = DWORD(mode.value | 4) + if not windll.kernel32.SetConsoleMode(stdout_handle, mode): + raise WinError() + except Exception: + should_color = False + STYLES["red"] = "\x1b[91m" if should_color else "" STYLES["green"] = "\x1b[92m" if should_color else "" STYLES["yellow"] = "\x1b[93m" if should_color else "" @@ -857,558 +910,621 @@ def get_git_branch() -> str: def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: str) -> None: class_name = class_def.name + with open( + os.devnull if dry_run else os.path.join(output_dir, f"class_{class_name.lower()}.rst"), + "w", + encoding="utf-8", + newline="\n", + ) as f: + # Remove the "Edit on Github" button from the online docs page. + f.write(":github_url: hide\n\n") + + # Add keywords metadata. + if class_def.keywords is not None and class_def.keywords != "": + f.write(f".. meta::\n\t:keywords: {class_def.keywords}\n\n") + + # Warn contributors not to edit this file directly. + # Also provide links to the source files for reference. + + git_branch = get_git_branch() + source_xml_path = os.path.relpath(class_def.filepath, root_directory).replace("\\", "/") + source_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/{source_xml_path}" + generator_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/doc/tools/make_rst.py" + + f.write(".. DO NOT EDIT THIS FILE!!!\n") + f.write(".. Generated automatically from Godot engine sources.\n") + f.write(f".. Generator: {generator_github_url}.\n") + f.write(f".. XML source: {source_github_url}.\n\n") + + # Document reference id and header. + f.write(f".. _class_{class_name}:\n\n") + f.write(make_heading(class_name, "=", False)) + + f.write(make_deprecated_experimental(class_def, state)) + + ### INHERITANCE TREE ### + + # Ascendants + if class_def.inherits: + inherits = class_def.inherits.strip() + f.write(f'**{translate("Inherits:")}** ') + first = True + while inherits in state.classes: + if not first: + f.write(" **<** ") + else: + first = False - if dry_run: - f = open(os.devnull, "w", encoding="utf-8") - else: - f = open(os.path.join(output_dir, f"class_{class_name.lower()}.rst"), "w", encoding="utf-8") - - # Remove the "Edit on Github" button from the online docs page. - f.write(":github_url: hide\n\n") - - # Warn contributors not to edit this file directly. - # Also provide links to the source files for reference. - - git_branch = get_git_branch() - source_xml_path = os.path.relpath(class_def.filepath, root_directory).replace("\\", "/") - source_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/{source_xml_path}" - generator_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/doc/tools/make_rst.py" - - f.write(".. DO NOT EDIT THIS FILE!!!\n") - f.write(".. Generated automatically from Godot engine sources.\n") - f.write(f".. Generator: {generator_github_url}.\n") - f.write(f".. XML source: {source_github_url}.\n\n") - - # Document reference id and header. - f.write(f".. _class_{class_name}:\n\n") - f.write(make_heading(class_name, "=", False)) - - ### INHERITANCE TREE ### - - # Ascendants - if class_def.inherits: - inherits = class_def.inherits.strip() - f.write(f'**{translate("Inherits:")}** ') - first = True - while inherits in state.classes: - if not first: - f.write(" **<** ") - else: - first = False - - f.write(make_type(inherits, state)) - inode = state.classes[inherits].inherits - if inode: - inherits = inode.strip() - else: - break - f.write("\n\n") - - # Descendants - inherited: List[str] = [] - for c in state.classes.values(): - if c.inherits and c.inherits.strip() == class_name: - inherited.append(c.name) + f.write(make_type(inherits, state)) + inode = state.classes[inherits].inherits + if inode: + inherits = inode.strip() + else: + break + f.write("\n\n") - if len(inherited): - f.write(f'**{translate("Inherited By:")}** ') - for i, child in enumerate(inherited): - if i > 0: - f.write(", ") - f.write(make_type(child, state)) - f.write("\n\n") + # Descendants + inherited: List[str] = [] + for c in state.classes.values(): + if c.inherits and c.inherits.strip() == class_name: + inherited.append(c.name) + + if len(inherited): + f.write(f'**{translate("Inherited By:")}** ') + for i, child in enumerate(inherited): + if i > 0: + f.write(", ") + f.write(make_type(child, state)) + f.write("\n\n") - ### INTRODUCTION ### + ### INTRODUCTION ### - has_description = False + has_description = False - # Brief description - if class_def.brief_description is not None and class_def.brief_description.strip() != "": - has_description = True + # Brief description + if class_def.brief_description is not None and class_def.brief_description.strip() != "": + has_description = True - f.write(f"{format_text_block(class_def.brief_description.strip(), class_def, state)}\n\n") + f.write(f"{format_text_block(class_def.brief_description.strip(), class_def, state)}\n\n") - # Class description - if class_def.description is not None and class_def.description.strip() != "": - has_description = True + # Class description + if class_def.description is not None and class_def.description.strip() != "": + has_description = True - f.write(".. rst-class:: classref-introduction-group\n\n") - f.write(make_heading("Description", "-")) + f.write(".. rst-class:: classref-introduction-group\n\n") + f.write(make_heading("Description", "-")) - f.write(f"{format_text_block(class_def.description.strip(), class_def, state)}\n\n") + f.write(f"{format_text_block(class_def.description.strip(), class_def, state)}\n\n") - if not has_description: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + if not has_description: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this class. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" ) - + "\n\n" - ) - if class_def.name in CLASSES_WITH_CSHARP_DIFFERENCES: - f.write(".. note::\n\n\t") - f.write( - translate( - "There are notable differences when using this API with C#. See :ref:`doc_c_sharp_differences` for more information." + if class_def.name in CLASSES_WITH_CSHARP_DIFFERENCES: + f.write(".. note::\n\n\t") + f.write( + translate( + "There are notable differences when using this API with C#. See :ref:`doc_c_sharp_differences` for more information." + ) + + "\n\n" ) - + "\n\n" - ) - # Online tutorials - if len(class_def.tutorials) > 0: - f.write(".. rst-class:: classref-introduction-group\n\n") - f.write(make_heading("Tutorials", "-")) + # Online tutorials + if len(class_def.tutorials) > 0: + f.write(".. rst-class:: classref-introduction-group\n\n") + f.write(make_heading("Tutorials", "-")) - for url, title in class_def.tutorials: - f.write(f"- {make_link(url, title)}\n\n") + for url, title in class_def.tutorials: + f.write(f"- {make_link(url, title)}\n\n") - ### REFERENCE TABLES ### + ### REFERENCE TABLES ### - # Reused container for reference tables. - ml: List[Tuple[Optional[str], ...]] = [] + # Reused container for reference tables. + ml: List[Tuple[Optional[str], ...]] = [] - # Properties reference table - if len(class_def.properties) > 0: - f.write(".. rst-class:: classref-reftable-group\n\n") - f.write(make_heading("Properties", "-")) + # Properties reference table + if len(class_def.properties) > 0: + f.write(".. rst-class:: classref-reftable-group\n\n") + f.write(make_heading("Properties", "-")) - ml = [] - for property_def in class_def.properties.values(): - type_rst = property_def.type_name.to_rst(state) - default = property_def.default_value - if default is not None and property_def.overrides: - ref = f":ref:`{property_def.overrides}<class_{property_def.overrides}_property_{property_def.name}>`" - # Not using translate() for now as it breaks table formatting. - ml.append((type_rst, property_def.name, f"{default} (overrides {ref})")) - else: - ref = f":ref:`{property_def.name}<class_{class_name}_property_{property_def.name}>`" - ml.append((type_rst, ref, default)) + ml = [] + for property_def in class_def.properties.values(): + type_rst = property_def.type_name.to_rst(state) + default = property_def.default_value + if default is not None and property_def.overrides: + ref = ( + f":ref:`{property_def.overrides}<class_{property_def.overrides}_property_{property_def.name}>`" + ) + # Not using translate() for now as it breaks table formatting. + ml.append((type_rst, property_def.name, f"{default} (overrides {ref})")) + else: + ref = f":ref:`{property_def.name}<class_{class_name}_property_{property_def.name}>`" + ml.append((type_rst, ref, default)) - format_table(f, ml, True) + format_table(f, ml, True) - # Constructors, Methods, Operators reference tables - if len(class_def.constructors) > 0: - f.write(".. rst-class:: classref-reftable-group\n\n") - f.write(make_heading("Constructors", "-")) + # Constructors, Methods, Operators reference tables + if len(class_def.constructors) > 0: + f.write(".. rst-class:: classref-reftable-group\n\n") + f.write(make_heading("Constructors", "-")) - ml = [] - for method_list in class_def.constructors.values(): - for m in method_list: - ml.append(make_method_signature(class_def, m, "constructor", state)) + ml = [] + for method_list in class_def.constructors.values(): + for m in method_list: + ml.append(make_method_signature(class_def, m, "constructor", state)) - format_table(f, ml) + format_table(f, ml) - if len(class_def.methods) > 0: - f.write(".. rst-class:: classref-reftable-group\n\n") - f.write(make_heading("Methods", "-")) + if len(class_def.methods) > 0: + f.write(".. rst-class:: classref-reftable-group\n\n") + f.write(make_heading("Methods", "-")) - ml = [] - for method_list in class_def.methods.values(): - for m in method_list: - ml.append(make_method_signature(class_def, m, "method", state)) + ml = [] + for method_list in class_def.methods.values(): + for m in method_list: + ml.append(make_method_signature(class_def, m, "method", state)) - format_table(f, ml) + format_table(f, ml) - if len(class_def.operators) > 0: - f.write(".. rst-class:: classref-reftable-group\n\n") - f.write(make_heading("Operators", "-")) + if len(class_def.operators) > 0: + f.write(".. rst-class:: classref-reftable-group\n\n") + f.write(make_heading("Operators", "-")) - ml = [] - for method_list in class_def.operators.values(): - for m in method_list: - ml.append(make_method_signature(class_def, m, "operator", state)) + ml = [] + for method_list in class_def.operators.values(): + for m in method_list: + ml.append(make_method_signature(class_def, m, "operator", state)) - format_table(f, ml) + format_table(f, ml) - # Theme properties reference table - if len(class_def.theme_items) > 0: - f.write(".. rst-class:: classref-reftable-group\n\n") - f.write(make_heading("Theme Properties", "-")) + # Theme properties reference table + if len(class_def.theme_items) > 0: + f.write(".. rst-class:: classref-reftable-group\n\n") + f.write(make_heading("Theme Properties", "-")) - ml = [] - for theme_item_def in class_def.theme_items.values(): - ref = f":ref:`{theme_item_def.name}<class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}>`" - ml.append((theme_item_def.type_name.to_rst(state), ref, theme_item_def.default_value)) + ml = [] + for theme_item_def in class_def.theme_items.values(): + ref = f":ref:`{theme_item_def.name}<class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}>`" + ml.append((theme_item_def.type_name.to_rst(state), ref, theme_item_def.default_value)) - format_table(f, ml, True) + format_table(f, ml, True) - ### DETAILED DESCRIPTIONS ### + ### DETAILED DESCRIPTIONS ### - # Signal descriptions - if len(class_def.signals) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Signals", "-")) + # Signal descriptions + if len(class_def.signals) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Signals", "-")) - index = 0 + index = 0 - for signal in class_def.signals.values(): - if index != 0: - f.write(make_separator()) + for signal in class_def.signals.values(): + if index != 0: + f.write(make_separator()) - # Create signal signature and anchor point. + # Create signal signature and anchor point. - f.write(f".. _class_{class_name}_signal_{signal.name}:\n\n") - f.write(".. rst-class:: classref-signal\n\n") + signal_anchor = f"class_{class_name}_signal_{signal.name}" + f.write(f".. _{signal_anchor}:\n\n") + self_link = f":ref:`🔗<{signal_anchor}>`" + f.write(".. rst-class:: classref-signal\n\n") - _, signature = make_method_signature(class_def, signal, "", state) - f.write(f"{signature}\n\n") + _, signature = make_method_signature(class_def, signal, "", state) + f.write(f"{signature} {self_link}\n\n") - # Add signal description, or a call to action if it's missing. + # Add signal description, or a call to action if it's missing. - if signal.description is not None and signal.description.strip() != "": - f.write(f"{format_text_block(signal.description.strip(), signal, state)}\n\n") - else: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this signal. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + f.write(make_deprecated_experimental(signal, state)) + + if signal.description is not None and signal.description.strip() != "": + f.write(f"{format_text_block(signal.description.strip(), signal, state)}\n\n") + elif signal.deprecated is None and signal.experimental is None: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this signal. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" ) - + "\n\n" - ) - index += 1 + index += 1 - # Enumeration descriptions - if len(class_def.enums) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Enumerations", "-")) + # Enumeration descriptions + if len(class_def.enums) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Enumerations", "-")) - index = 0 + index = 0 + + for e in class_def.enums.values(): + if index != 0: + f.write(make_separator()) - for e in class_def.enums.values(): - if index != 0: - f.write(make_separator()) + # Create enumeration signature and anchor point. - # Create enumeration signature and anchor point. + enum_anchor = f"enum_{class_name}_{e.name}" + f.write(f".. _{enum_anchor}:\n\n") + self_link = f":ref:`🔗<{enum_anchor}>`" + f.write(".. rst-class:: classref-enumeration\n\n") - f.write(f".. _enum_{class_name}_{e.name}:\n\n") - f.write(".. rst-class:: classref-enumeration\n\n") + if e.is_bitfield: + f.write(f"flags **{e.name}**: {self_link}\n\n") + else: + f.write(f"enum **{e.name}**: {self_link}\n\n") - if e.is_bitfield: - f.write(f"flags **{e.name}**:\n\n") - else: - f.write(f"enum **{e.name}**:\n\n") + for value in e.values.values(): + # Also create signature and anchor point for each enum constant. - for value in e.values.values(): - # Also create signature and anchor point for each enum constant. + f.write(f".. _class_{class_name}_constant_{value.name}:\n\n") + f.write(".. rst-class:: classref-enumeration-constant\n\n") - f.write(f".. _class_{class_name}_constant_{value.name}:\n\n") - f.write(".. rst-class:: classref-enumeration-constant\n\n") + f.write(f"{e.type_name.to_rst(state)} **{value.name}** = ``{value.value}``\n\n") - f.write(f"{e.type_name.to_rst(state)} **{value.name}** = ``{value.value}``\n\n") + # Add enum constant description. - # Add enum constant description. + f.write(make_deprecated_experimental(value, state)) - if value.text is not None and value.text.strip() != "": - f.write(f"{format_text_block(value.text.strip(), value, state)}") - else: + if value.text is not None and value.text.strip() != "": + f.write(f"{format_text_block(value.text.strip(), value, state)}") + elif value.deprecated is None and value.experimental is None: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this enum. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" + ) + + f.write("\n\n") + + index += 1 + + # Constant descriptions + if len(class_def.constants) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Constants", "-")) + + for constant in class_def.constants.values(): + # Create constant signature and anchor point. + + constant_anchor = f"class_{class_name}_constant_{constant.name}" + f.write(f".. _{constant_anchor}:\n\n") + self_link = f":ref:`🔗<{constant_anchor}>`" + f.write(".. rst-class:: classref-constant\n\n") + + f.write(f"**{constant.name}** = ``{constant.value}`` {self_link}\n\n") + + # Add constant description. + + f.write(make_deprecated_experimental(constant, state)) + + if constant.text is not None and constant.text.strip() != "": + f.write(f"{format_text_block(constant.text.strip(), constant, state)}") + elif constant.deprecated is None and constant.experimental is None: f.write(".. container:: contribute\n\n\t") f.write( translate( - "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 constant. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" ) + "\n\n" ) f.write("\n\n") - index += 1 + # Annotation descriptions + if len(class_def.annotations) > 0: + f.write(make_separator(True)) + f.write(make_heading("Annotations", "-")) - # Constant descriptions - if len(class_def.constants) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Constants", "-")) + index = 0 - for constant in class_def.constants.values(): - # Create constant signature and anchor point. + for method_list in class_def.annotations.values(): # type: ignore + for i, m in enumerate(method_list): + if index != 0: + f.write(make_separator()) - f.write(f".. _class_{class_name}_constant_{constant.name}:\n\n") - f.write(".. rst-class:: classref-constant\n\n") + # Create annotation signature and anchor point. - f.write(f"**{constant.name}** = ``{constant.value}``\n\n") + self_link = "" + if i == 0: + annotation_anchor = f"class_{class_name}_annotation_{m.name}" + f.write(f".. _{annotation_anchor}:\n\n") + self_link = f" :ref:`🔗<{annotation_anchor}>`" - # Add enum constant description. + f.write(".. rst-class:: classref-annotation\n\n") - if constant.text is not None and constant.text.strip() != "": - f.write(f"{format_text_block(constant.text.strip(), constant, state)}") - else: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this constant. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" - ) - + "\n\n" - ) + _, signature = make_method_signature(class_def, m, "", state) + f.write(f"{signature}{self_link}\n\n") - f.write("\n\n") + # Add annotation description, or a call to action if it's missing. + + if m.description is not None and m.description.strip() != "": + f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") + else: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this annotation. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" + ) - # Annotation descriptions - if len(class_def.annotations) > 0: - f.write(make_separator(True)) - f.write(make_heading("Annotations", "-")) + index += 1 - index = 0 + # Property descriptions + if any(not p.overrides for p in class_def.properties.values()) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Property Descriptions", "-")) + + index = 0 + + for property_def in class_def.properties.values(): + if property_def.overrides: + continue - for method_list in class_def.annotations.values(): # type: ignore - for i, m in enumerate(method_list): if index != 0: f.write(make_separator()) - # Create annotation signature and anchor point. + # Create property signature and anchor point. + + property_anchor = f"class_{class_name}_property_{property_def.name}" + f.write(f".. _{property_anchor}:\n\n") + self_link = f":ref:`🔗<{property_anchor}>`" + f.write(".. rst-class:: classref-property\n\n") + + property_default = "" + if property_def.default_value is not None: + property_default = f" = {property_def.default_value}" + f.write( + f"{property_def.type_name.to_rst(state)} **{property_def.name}**{property_default} {self_link}\n\n" + ) - if i == 0: - f.write(f".. _class_{class_name}_annotation_{m.name}:\n\n") + # Create property setter and getter records. - f.write(".. rst-class:: classref-annotation\n\n") + property_setget = "" - _, signature = make_method_signature(class_def, m, "", state) - f.write(f"{signature}\n\n") + if property_def.setter is not None and not property_def.setter.startswith("_"): + property_setter = make_setter_signature(class_def, property_def, state) + property_setget += f"- {property_setter}\n" - # Add annotation description, or a call to action if it's missing. + if property_def.getter is not None and not property_def.getter.startswith("_"): + property_getter = make_getter_signature(class_def, property_def, state) + property_setget += f"- {property_getter}\n" - if m.description is not None and m.description.strip() != "": - f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") - else: + if property_setget != "": + f.write(".. rst-class:: classref-property-setget\n\n") + f.write(property_setget) + f.write("\n") + + # Add property description, or a call to action if it's missing. + + f.write(make_deprecated_experimental(property_def, state)) + + if property_def.text is not None and property_def.text.strip() != "": + f.write(f"{format_text_block(property_def.text.strip(), property_def, state)}\n\n") + if property_def.type_name.type_name in PACKED_ARRAY_TYPES: + tmp = f"[b]Note:[/b] The returned array is [i]copied[/i] and any changes to it will not update the original property value. See [{property_def.type_name.type_name}] for more details." + f.write(f"{format_text_block(tmp, property_def, state)}\n\n") + elif property_def.deprecated is None and property_def.experimental is None: f.write(".. container:: contribute\n\n\t") f.write( translate( - "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>`!" ) + "\n\n" ) index += 1 - # Property descriptions - if any(not p.overrides for p in class_def.properties.values()) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Property Descriptions", "-")) - - index = 0 + # Constructor, Method, Operator descriptions + if len(class_def.constructors) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Constructor Descriptions", "-")) - for property_def in class_def.properties.values(): - if property_def.overrides: - continue + index = 0 - if index != 0: - f.write(make_separator()) + for method_list in class_def.constructors.values(): + for i, m in enumerate(method_list): + if index != 0: + f.write(make_separator()) - # Create property signature and anchor point. + # Create constructor signature and anchor point. - f.write(f".. _class_{class_name}_property_{property_def.name}:\n\n") - f.write(".. rst-class:: classref-property\n\n") + self_link = "" + if i == 0: + constructor_anchor = f"class_{class_name}_constructor_{m.name}" + f.write(f".. _{constructor_anchor}:\n\n") + self_link = f" :ref:`🔗<{constructor_anchor}>`" - property_default = "" - if property_def.default_value is not None: - property_default = f" = {property_def.default_value}" - f.write(f"{property_def.type_name.to_rst(state)} **{property_def.name}**{property_default}\n\n") + f.write(".. rst-class:: classref-constructor\n\n") - # Create property setter and getter records. + ret_type, signature = make_method_signature(class_def, m, "", state) + f.write(f"{ret_type} {signature}{self_link}\n\n") - property_setget = "" + # Add constructor description, or a call to action if it's missing. - if property_def.setter is not None and not property_def.setter.startswith("_"): - property_setter = make_setter_signature(class_def, property_def, state) - property_setget += f"- {property_setter}\n" + f.write(make_deprecated_experimental(m, state)) - if property_def.getter is not None and not property_def.getter.startswith("_"): - property_getter = make_getter_signature(class_def, property_def, state) - property_setget += f"- {property_getter}\n" + if m.description is not None and m.description.strip() != "": + f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") + elif m.deprecated is None and m.experimental is None: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this constructor. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" + ) - if property_setget != "": - f.write(".. rst-class:: classref-property-setget\n\n") - f.write(property_setget) - f.write("\n") + index += 1 - # Add property description, or a call to action if it's missing. + if len(class_def.methods) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Method Descriptions", "-")) - if property_def.text is not None and property_def.text.strip() != "": - f.write(f"{format_text_block(property_def.text.strip(), property_def, state)}\n\n") - else: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" - ) - + "\n\n" - ) + index = 0 - index += 1 + for method_list in class_def.methods.values(): + for i, m in enumerate(method_list): + if index != 0: + f.write(make_separator()) - # Constructor, Method, Operator descriptions - if len(class_def.constructors) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Constructor Descriptions", "-")) + # Create method signature and anchor point. - index = 0 + self_link = "" - for method_list in class_def.constructors.values(): - for i, m in enumerate(method_list): - if index != 0: - f.write(make_separator()) + if i == 0: + method_qualifier = "" + if m.name.startswith("_"): + method_qualifier = "private_" + method_anchor = f"class_{class_name}_{method_qualifier}method_{m.name}" + f.write(f".. _{method_anchor}:\n\n") + self_link = f" :ref:`🔗<{method_anchor}>`" - # Create constructor signature and anchor point. + f.write(".. rst-class:: classref-method\n\n") - if i == 0: - f.write(f".. _class_{class_name}_constructor_{m.name}:\n\n") + ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(".. rst-class:: classref-constructor\n\n") + f.write(f"{ret_type} {signature}{self_link}\n\n") - ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(f"{ret_type} {signature}\n\n") + # Add method description, or a call to action if it's missing. - # Add constructor description, or a call to action if it's missing. + f.write(make_deprecated_experimental(m, state)) - if m.description is not None and m.description.strip() != "": - f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") - else: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this constructor. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + if m.description is not None and m.description.strip() != "": + f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") + elif m.deprecated is None and m.experimental is None: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this method. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" ) - + "\n\n" - ) - index += 1 + index += 1 - if len(class_def.methods) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Method Descriptions", "-")) + if len(class_def.operators) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Operator Descriptions", "-")) - index = 0 + index = 0 - for method_list in class_def.methods.values(): - for i, m in enumerate(method_list): - if index != 0: - f.write(make_separator()) + for method_list in class_def.operators.values(): + for i, m in enumerate(method_list): + if index != 0: + f.write(make_separator()) - # Create method signature and anchor point. + # Create operator signature and anchor point. - if i == 0: - method_qualifier = "" - if m.name.startswith("_"): - method_qualifier = "private_" + operator_anchor = f"class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" + for parameter in m.parameters: + operator_anchor += f"_{parameter.type_name.type_name}" + f.write(f".. _{operator_anchor}:\n\n") + self_link = f":ref:`🔗<{operator_anchor}>`" - f.write(f".. _class_{class_name}_{method_qualifier}method_{m.name}:\n\n") + f.write(".. rst-class:: classref-operator\n\n") - f.write(".. rst-class:: classref-method\n\n") + ret_type, signature = make_method_signature(class_def, m, "", state) + f.write(f"{ret_type} {signature} {self_link}\n\n") - ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(f"{ret_type} {signature}\n\n") + # Add operator description, or a call to action if it's missing. - # Add method description, or a call to action if it's missing. + f.write(make_deprecated_experimental(m, state)) - if m.description is not None and m.description.strip() != "": - f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") - else: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this method. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + if m.description is not None and m.description.strip() != "": + f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") + elif m.deprecated is None and m.experimental is None: + f.write(".. container:: contribute\n\n\t") + f.write( + translate( + "There is currently no description for this operator. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + ) + + "\n\n" ) - + "\n\n" - ) - index += 1 + index += 1 - if len(class_def.operators) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Operator Descriptions", "-")) + # Theme property descriptions + if len(class_def.theme_items) > 0: + f.write(make_separator(True)) + f.write(".. rst-class:: classref-descriptions-group\n\n") + f.write(make_heading("Theme Property Descriptions", "-")) - index = 0 + index = 0 - for method_list in class_def.operators.values(): - for i, m in enumerate(method_list): + for theme_item_def in class_def.theme_items.values(): if index != 0: f.write(make_separator()) - # Create operator signature and anchor point. + # Create theme property signature and anchor point. - operator_anchor = f".. _class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" - for parameter in m.parameters: - operator_anchor += f"_{parameter.type_name.type_name}" - operator_anchor += f":\n\n" - f.write(operator_anchor) + theme_item_anchor = f"class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}" + f.write(f".. _{theme_item_anchor}:\n\n") + self_link = f":ref:`🔗<{theme_item_anchor}>`" + f.write(".. rst-class:: classref-themeproperty\n\n") - f.write(".. rst-class:: classref-operator\n\n") + theme_item_default = "" + if theme_item_def.default_value is not None: + theme_item_default = f" = {theme_item_def.default_value}" + f.write( + f"{theme_item_def.type_name.to_rst(state)} **{theme_item_def.name}**{theme_item_default} {self_link}\n\n" + ) - ret_type, signature = make_method_signature(class_def, m, "", state) - f.write(f"{ret_type} {signature}\n\n") + # Add theme property description, or a call to action if it's missing. - # Add operator description, or a call to action if it's missing. + f.write(make_deprecated_experimental(theme_item_def, state)) - if m.description is not None and m.description.strip() != "": - f.write(f"{format_text_block(m.description.strip(), m, state)}\n\n") - else: + if theme_item_def.text is not None and theme_item_def.text.strip() != "": + f.write(f"{format_text_block(theme_item_def.text.strip(), theme_item_def, state)}\n\n") + elif theme_item_def.deprecated is None and theme_item_def.experimental is None: f.write(".. container:: contribute\n\n\t") f.write( translate( - "There is currently no description for this operator. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" + "There is currently no description for this theme property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" ) + "\n\n" ) index += 1 - # Theme property descriptions - if len(class_def.theme_items) > 0: - f.write(make_separator(True)) - f.write(".. rst-class:: classref-descriptions-group\n\n") - f.write(make_heading("Theme Property Descriptions", "-")) - - index = 0 - - for theme_item_def in class_def.theme_items.values(): - if index != 0: - f.write(make_separator()) - - # Create theme property signature and anchor point. - - f.write(f".. _class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}:\n\n") - f.write(".. rst-class:: classref-themeproperty\n\n") - - theme_item_default = "" - if theme_item_def.default_value is not None: - theme_item_default = f" = {theme_item_def.default_value}" - f.write(f"{theme_item_def.type_name.to_rst(state)} **{theme_item_def.name}**{theme_item_default}\n\n") - - # Add theme property description, or a call to action if it's missing. - - if theme_item_def.text is not None and theme_item_def.text.strip() != "": - f.write(f"{format_text_block(theme_item_def.text.strip(), theme_item_def, state)}\n\n") - else: - f.write(".. container:: contribute\n\n\t") - f.write( - translate( - "There is currently no description for this theme property. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!" - ) - + "\n\n" - ) - - index += 1 - - f.write(make_footer()) + f.write(make_footer()) def make_type(klass: str, state: State) -> str: if klass.find("*") != -1: # Pointer, ignore - return klass + return f"``{klass}``" + link_type = klass + is_array = False + if link_type.endswith("[]"): # Typed array, strip [] to link to contained type. link_type = link_type[:-2] + is_array = True + if link_type in state.classes: - return f":ref:`{klass}<class_{link_type}>`" - print_error(f'{state.current_class}.xml: Unresolved type "{klass}".', state) - return klass + type_rst = f":ref:`{link_type}<class_{link_type}>`" + if is_array: + type_rst = f":ref:`Array<class_Array>`\\[{type_rst}\\]" + return type_rst + + print_error(f'{state.current_class}.xml: Unresolved type "{link_type}".', state) + type_rst = f"``{link_type}``" + if is_array: + type_rst = f":ref:`Array<class_Array>`\\[{type_rst}\\]" + return type_rst def make_enum(t: str, is_bitfield: bool, state: State) -> str: @@ -1430,7 +1546,7 @@ def make_enum(t: str, is_bitfield: bool, state: State) -> str: if is_bitfield: if not state.classes[c].enums[e].is_bitfield: print_error(f'{state.current_class}.xml: Enum "{t}" is not bitfield.', state) - return f"|bitfield|\\<:ref:`{e}<enum_{c}_{e}>`\\>" + return f"|bitfield|\\[:ref:`{e}<enum_{c}_{e}>`\\]" else: return f":ref:`{e}<enum_{c}_{e}>`" @@ -1460,36 +1576,36 @@ def make_method_signature( out += f":ref:`{op_name}<class_{class_def.name}_{ref_type}_{sanitize_operator_name(definition.name, state)}" for parameter in definition.parameters: out += f"_{parameter.type_name.type_name}" - out += f">` " + out += ">`" elif ref_type == "method": ref_type_qualifier = "" if definition.name.startswith("_"): ref_type_qualifier = "private_" - out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type_qualifier}{ref_type}_{definition.name}>` " + out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type_qualifier}{ref_type}_{definition.name}>`" else: - out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type}_{definition.name}>` " + out += f":ref:`{definition.name}<class_{class_def.name}_{ref_type}_{definition.name}>`" else: - out += f"**{definition.name}** " + out += f"**{definition.name}**" - out += "**(**" + out += "\\ (" for i, arg in enumerate(definition.parameters): if i > 0: out += ", " else: - out += " " + out += "\\ " - out += f"{arg.type_name.to_rst(state)} {arg.name}" + out += f"{arg.name}\\: {arg.type_name.to_rst(state)}" if arg.default_value is not None: - out += f"={arg.default_value}" + out += f" = {arg.default_value}" if qualifiers is not None and "vararg" in qualifiers: if len(definition.parameters) > 0: out += ", ..." else: - out += " ..." + out += "\\ ..." - out += " **)**" + out += "\\ )" if qualifiers is not None: # Use substitutions for abbreviations. This is used to display tooltips on hover. @@ -1533,6 +1649,28 @@ def make_getter_signature(class_def: ClassDef, property_def: PropertyDef, state: return f"{ret_type} {signature}" +def make_deprecated_experimental(item: DefinitionBase, state: State) -> str: + result = "" + + if item.deprecated is not None: + deprecated_prefix = translate("Deprecated:") + if item.deprecated.strip() == "": + default_message = translate(f"This {item.definition_name} may be changed or removed in future versions.") + result += f"**{deprecated_prefix}** {default_message}\n\n" + else: + result += f"**{deprecated_prefix}** {format_text_block(item.deprecated.strip(), item, state)}\n\n" + + if item.experimental is not None: + experimental_prefix = translate("Experimental:") + if item.experimental.strip() == "": + default_message = translate(f"This {item.definition_name} may be changed or removed in future versions.") + result += f"**{experimental_prefix}** {default_message}\n\n" + else: + result += f"**{experimental_prefix}** {format_text_block(item.experimental.strip(), item, state)}\n\n" + + return result + + def make_heading(title: str, underline: str, l10n: bool = True) -> str: if l10n: new_title = translate(title) @@ -1554,6 +1692,7 @@ def make_footer() -> str: ) operator_msg = translate("This method describes a valid operator to use with this type as left-hand operand.") bitfield_msg = translate("This value is an integer composed as a bitmask of the following flags.") + void_msg = translate("No return value.") return ( f".. |virtual| replace:: :abbr:`virtual ({virtual_msg})`\n" @@ -1563,6 +1702,7 @@ def make_footer() -> str: f".. |static| replace:: :abbr:`static ({static_msg})`\n" f".. |operator| replace:: :abbr:`operator ({operator_msg})`\n" f".. |bitfield| replace:: :abbr:`BitField ({bitfield_msg})`\n" + f".. |void| replace:: :abbr:`void ({void_msg})`\n" ) @@ -1600,54 +1740,52 @@ def make_link(url: str, title: str) -> str: def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_dir: str) -> None: - if dry_run: - f = open(os.devnull, "w", encoding="utf-8") - else: - f = open(os.path.join(output_dir, "index.rst"), "w", encoding="utf-8") + with open( + os.devnull if dry_run else os.path.join(output_dir, "index.rst"), "w", encoding="utf-8", newline="\n" + ) as f: + # Remove the "Edit on Github" button from the online docs page, and disallow user-contributed notes + # on the index page. User-contributed notes are allowed on individual class pages. + f.write(":github_url: hide\n:allow_comments: False\n\n") - # Remove the "Edit on Github" button from the online docs page, and disallow user-contributed notes - # on the index page. User-contributed notes are allowed on individual class pages. - f.write(":github_url: hide\n:allow_comments: False\n\n") + # Warn contributors not to edit this file directly. + # Also provide links to the source files for reference. - # Warn contributors not to edit this file directly. - # Also provide links to the source files for reference. + git_branch = get_git_branch() + generator_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/doc/tools/make_rst.py" - git_branch = get_git_branch() - generator_github_url = f"https://github.com/godotengine/godot/tree/{git_branch}/doc/tools/make_rst.py" + f.write(".. DO NOT EDIT THIS FILE!!!\n") + f.write(".. Generated automatically from Godot engine sources.\n") + f.write(f".. Generator: {generator_github_url}.\n\n") - f.write(".. DO NOT EDIT THIS FILE!!!\n") - f.write(".. Generated automatically from Godot engine sources.\n") - f.write(f".. Generator: {generator_github_url}.\n\n") + f.write(".. _doc_class_reference:\n\n") - f.write(".. _doc_class_reference:\n\n") + f.write(make_heading("All classes", "=")) - f.write(make_heading("All classes", "=")) + for group_name in CLASS_GROUPS: + if group_name in grouped_classes: + f.write(make_heading(CLASS_GROUPS[group_name], "=")) - for group_name in CLASS_GROUPS: - if group_name in grouped_classes: - f.write(make_heading(CLASS_GROUPS[group_name], "=")) - - f.write(".. toctree::\n") - f.write(" :maxdepth: 1\n") - f.write(f" :name: toc-class-ref-{group_name}s\n") - f.write("\n") + f.write(".. toctree::\n") + f.write(" :maxdepth: 1\n") + f.write(f" :name: toc-class-ref-{group_name}s\n") + f.write("\n") - if group_name in CLASS_GROUPS_BASE: - f.write(f" class_{CLASS_GROUPS_BASE[group_name].lower()}\n") + if group_name in CLASS_GROUPS_BASE: + f.write(f" class_{CLASS_GROUPS_BASE[group_name].lower()}\n") - for class_name in grouped_classes[group_name]: - if group_name in CLASS_GROUPS_BASE and CLASS_GROUPS_BASE[group_name].lower() == class_name.lower(): - continue + for class_name in grouped_classes[group_name]: + if group_name in CLASS_GROUPS_BASE and CLASS_GROUPS_BASE[group_name].lower() == class_name.lower(): + continue - f.write(f" class_{class_name.lower()}\n") + f.write(f" class_{class_name.lower()}\n") - f.write("\n") + f.write("\n") # Formatting helpers. -RESERVED_FORMATTING_TAGS = ["i", "b", "u", "code", "kbd", "center", "url", "br"] +RESERVED_FORMATTING_TAGS = ["i", "b", "u", "lb", "rb", "code", "kbd", "center", "url", "br"] RESERVED_LAYOUT_TAGS = ["codeblocks"] RESERVED_CODEBLOCK_TAGS = ["codeblock", "gdscript", "csharp"] RESERVED_CROSSLINK_TAGS = [ @@ -1672,7 +1810,7 @@ def is_in_tagset(tag_text: str, tagset: List[str]) -> bool: # Tag with arguments. if tag_text.startswith(tag + " "): return True - # Tag with arguments, special case for [url]. + # Tag with arguments, special case for [url], [color], and [font]. if tag_text.startswith(tag + "="): return True @@ -1681,17 +1819,22 @@ def is_in_tagset(tag_text: str, tagset: List[str]) -> bool: def get_tag_and_args(tag_text: str) -> TagState: tag_name = tag_text - arguments: List[str] = [] + arguments: str = "" + + delim_pos = -1 + + space_pos = tag_text.find(" ") + if space_pos >= 0: + delim_pos = space_pos + # Special case for [url], [color], and [font]. assign_pos = tag_text.find("=") - if assign_pos >= 0: - tag_name = tag_text[:assign_pos] - arguments = [tag_text[assign_pos + 1 :].strip()] - else: - space_pos = tag_text.find(" ") - if space_pos >= 0: - tag_name = tag_text[:space_pos] - arguments = [tag_text[space_pos + 1 :].strip()] + if assign_pos >= 0 and (delim_pos < 0 or assign_pos < delim_pos): + delim_pos = assign_pos + + if delim_pos >= 0: + tag_name = tag_text[:delim_pos] + arguments = tag_text[delim_pos + 1 :].strip() closing = False if tag_name.startswith("/"): @@ -1879,11 +2022,14 @@ def format_text_block( state, ) - tag_text = "\n::\n" + if "lang=text" in tag_state.arguments.split(" "): + tag_text = "\n.. code:: text\n" + else: + tag_text = "\n::\n" inside_code = True inside_code_tag = tag_state.name - ignore_code_warnings = "skip-lint" in tag_state.arguments + ignore_code_warnings = "skip-lint" in tag_state.arguments.split(" ") elif is_in_tagset(tag_state.name, ["code"]): tag_text = "``" @@ -1891,7 +2037,7 @@ def format_text_block( inside_code = True inside_code_tag = "code" - ignore_code_warnings = "skip-lint" in tag_state.arguments + ignore_code_warnings = "skip-lint" in tag_state.arguments.split(" ") escape_pre = True if not ignore_code_warnings: @@ -1956,7 +2102,7 @@ def format_text_block( elif target_name in class_def.theme_items: print_warning( - f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} theme item in {context_name}. {code_warning_if_intended_string}', + f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} theme property in {context_name}. {code_warning_if_intended_string}', state, ) @@ -1988,7 +2134,7 @@ def format_text_block( # Cross-references to items in this or other class documentation pages. elif is_in_tagset(tag_state.name, RESERVED_CROSSLINK_TAGS): - link_target: str = tag_state.arguments[0] if len(tag_state.arguments) > 0 else "" + link_target: str = tag_state.arguments if link_target == "": print_error( @@ -2067,7 +2213,7 @@ def format_text_block( elif tag_state.name == "theme_item": if target_name not in class_def.theme_items: print_error( - f'{state.current_class}.xml: Unresolved theme item reference "{link_target}" in {context_name}.', + f'{state.current_class}.xml: Unresolved theme property reference "{link_target}" in {context_name}.', state, ) else: @@ -2148,7 +2294,7 @@ def format_text_block( # Formatting directives. elif is_in_tagset(tag_state.name, ["url"]): - url_target = tag_state.arguments[0] if len(tag_state.arguments) > 0 else "" + url_target = tag_state.arguments if url_target == "": print_error( @@ -2221,6 +2367,12 @@ def format_text_block( escape_pre = True tag_text = "" + elif tag_state.name == "lb": + tag_text = "\\[" + + elif tag_state.name == "rb": + tag_text = "\\]" + elif tag_state.name == "kbd": tag_text = "`" if tag_state.closing: @@ -2343,7 +2495,7 @@ def format_codeblock( opening_formatted = tag_state.name if len(tag_state.arguments) > 0: - opening_formatted += " " + " ".join(tag_state.arguments) + opening_formatted += " " + tag_state.arguments code_text = post_text[len(f"[{opening_formatted}]") : end_pos] post_text = post_text[end_pos:] |