diff --git a/extension/doc_tools/make_rst.py b/extension/doc_tools/make_rst.py index 5689df4f..7c269f7e 100644 --- a/extension/doc_tools/make_rst.py +++ b/extension/doc_tools/make_rst.py @@ -10,9 +10,9 @@ from collections import OrderedDict 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 +sys.path.insert(0, root_directory := os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + +from misc.utility.color import Ansi, toggle_color # $DOCS_URL/path/to/page.html(#fragment-tag) GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") @@ -89,8 +89,6 @@ ] strings_l10n: Dict[str, str] = {} -STYLES: Dict[str, str] = {} - CLASS_GROUPS: Dict[str, str] = { "global": "Globals", "node": "Nodes", @@ -149,7 +147,7 @@ "PackedByteArray", "PackedColorArray", "PackedFloat32Array", - "Packedfloat64Array", + "PackedFloat64Array", "PackedInt32Array", "PackedInt64Array", "PackedStringArray", @@ -705,31 +703,8 @@ def main() -> None: ) args = parser.parse_args() - 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). - # - 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 "" - STYLES["bold"] = "\x1b[1m" if should_color else "" - STYLES["regular"] = "\x1b[22m" if should_color else "" - STYLES["reset"] = "\x1b[0m" if should_color else "" + if args.color: + toggle_color(True) # Retrieve heading translations for the given language. if not args.dry_run and args.lang != "en": @@ -867,16 +842,16 @@ def main() -> None: if state.script_language_parity_check.hit_count > 0: if not args.verbose: print( - f'{STYLES["yellow"]}{state.script_language_parity_check.hit_count} code samples failed parity check. Use --verbose to get more information.{STYLES["reset"]}' + f"{Ansi.YELLOW}{state.script_language_parity_check.hit_count} code samples failed parity check. Use --verbose to get more information.{Ansi.RESET}" ) else: print( - f'{STYLES["yellow"]}{state.script_language_parity_check.hit_count} code samples failed parity check:{STYLES["reset"]}' + f"{Ansi.YELLOW}{state.script_language_parity_check.hit_count} code samples failed parity check:{Ansi.RESET}" ) for class_name in state.script_language_parity_check.hit_map.keys(): class_hits = state.script_language_parity_check.hit_map[class_name] - print(f'{STYLES["yellow"]}- {len(class_hits)} hits in class "{class_name}"{STYLES["reset"]}') + print(f'{Ansi.YELLOW}- {len(class_hits)} hits in class "{class_name}"{Ansi.RESET}') for context, error in class_hits: print(f" - {error} in {format_context_name(context)}") @@ -886,24 +861,22 @@ def main() -> None: if state.num_warnings >= 2: print( - f'{STYLES["yellow"]}{state.num_warnings} warnings were found in the class reference XML. Please check the messages above.{STYLES["reset"]}' + f"{Ansi.YELLOW}{state.num_warnings} warnings were found in the class reference XML. Please check the messages above.{Ansi.RESET}" ) elif state.num_warnings == 1: print( - f'{STYLES["yellow"]}1 warning was found in the class reference XML. Please check the messages above.{STYLES["reset"]}' + f"{Ansi.YELLOW}1 warning was found in the class reference XML. Please check the messages above.{Ansi.RESET}" ) if state.num_errors >= 2: print( - f'{STYLES["red"]}{state.num_errors} errors were found in the class reference XML. Please check the messages above.{STYLES["reset"]}' + f"{Ansi.RED}{state.num_errors} errors were found in the class reference XML. Please check the messages above.{Ansi.RESET}" ) elif state.num_errors == 1: - print( - f'{STYLES["red"]}1 error was found in the class reference XML. Please check the messages above.{STYLES["reset"]}' - ) + print(f"{Ansi.RED}1 error was found in the class reference XML. Please check the messages above.{Ansi.RESET}") if state.num_warnings == 0 and state.num_errors == 0: - print(f'{STYLES["green"]}No warnings or errors found in the class reference XML.{STYLES["reset"]}') + print(f"{Ansi.GREEN}No warnings or errors found in the class reference XML.{Ansi.RESET}") if not args.dry_run: print(f"Wrote reStructuredText files for each class to: {args.output}") else: @@ -914,12 +887,12 @@ def main() -> None: def print_error(error: str, state: State) -> None: - print(f'{STYLES["red"]}{STYLES["bold"]}ERROR:{STYLES["regular"]} {error}{STYLES["reset"]}') + print(f"{Ansi.RED}{Ansi.BOLD}ERROR:{Ansi.REGULAR} {error}{Ansi.RESET}") state.num_errors += 1 def print_warning(warning: str, state: State) -> None: - print(f'{STYLES["yellow"]}{STYLES["bold"]}WARNING:{STYLES["regular"]} {warning}{STYLES["reset"]}') + print(f"{Ansi.YELLOW}{Ansi.BOLD}WARNING:{Ansi.REGULAR} {warning}{Ansi.RESET}") state.num_warnings += 1 @@ -941,7 +914,7 @@ 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"), + os.devnull if dry_run else os.path.join(output_dir, f"class_{sanitize_class_name(class_name, True)}.rst"), "w", encoding="utf-8", newline="\n", @@ -967,7 +940,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: 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(f".. _class_{sanitize_class_name(class_name)}:\n\n") f.write(make_heading(class_name, "=", False)) f.write(make_deprecated_experimental(class_def, state)) @@ -977,15 +950,19 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Ascendants if class_def.inherits: inherits = class_def.inherits.strip() - f.write(f'**{translate("Inherits:")}** ') + f.write(f"**{translate('Inherits:')}** ") first = True - while inherits in state.classes: + while inherits is not None: if not first: f.write(" **<** ") else: first = False f.write(make_type(inherits, state)) + + if inherits not in state.classes: + break # Parent unknown. + inode = state.classes[inherits].inherits if inode: inherits = inode.strip() @@ -1000,7 +977,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: inherited.append(c.name) if len(inherited): - f.write(f'**{translate("Inherited By:")}** ') + f.write(f"**{translate('Inherited By:')}** ") for i, child in enumerate(inherited): if i > 0: f.write(", ") @@ -1067,13 +1044,11 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: 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}`" - ) + ref = f":ref:`{property_def.overrides}`" # 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}`" + ref = f":ref:`{property_def.name}`" ml.append((type_rst, ref, default)) format_table(f, ml, True) @@ -1119,7 +1094,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: ml = [] for theme_item_def in class_def.theme_items.values(): - ref = f":ref:`{theme_item_def.name}`" + ref = f":ref:`{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) @@ -1140,7 +1115,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create signal signature and anchor point. - signal_anchor = f"class_{class_name}_signal_{signal.name}" + signal_anchor = f"class_{sanitize_class_name(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") @@ -1179,7 +1154,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create enumeration signature and anchor point. - enum_anchor = f"enum_{class_name}_{e.name}" + enum_anchor = f"enum_{sanitize_class_name(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") @@ -1192,7 +1167,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: 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(f".. _class_{sanitize_class_name(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") @@ -1225,7 +1200,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: for constant in class_def.constants.values(): # Create constant signature and anchor point. - constant_anchor = f"class_{class_name}_constant_{constant.name}" + constant_anchor = f"class_{sanitize_class_name(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") @@ -1265,7 +1240,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: self_link = "" if i == 0: - annotation_anchor = f"class_{class_name}_annotation_{m.name}" + annotation_anchor = f"class_{sanitize_class_name(class_name)}_annotation_{m.name}" f.write(f".. _{annotation_anchor}:\n\n") self_link = f" :ref:`🔗<{annotation_anchor}>`" @@ -1306,7 +1281,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create property signature and anchor point. - property_anchor = f"class_{class_name}_property_{property_def.name}" + property_anchor = f"class_{sanitize_class_name(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") @@ -1341,9 +1316,6 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: 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( @@ -1353,6 +1325,11 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: + "\n\n" ) + # Add copy note to built-in properties returning `Packed*Array`. + if property_def.type_name.type_name in PACKED_ARRAY_TYPES: + copy_note = 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(copy_note, property_def, state)}\n\n") + index += 1 # Constructor, Method, Operator descriptions @@ -1372,7 +1349,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: self_link = "" if i == 0: - constructor_anchor = f"class_{class_name}_constructor_{m.name}" + constructor_anchor = f"class_{sanitize_class_name(class_name)}_constructor_{m.name}" f.write(f".. _{constructor_anchor}:\n\n") self_link = f" :ref:`🔗<{constructor_anchor}>`" @@ -1418,7 +1395,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: method_qualifier = "" if m.name.startswith("_"): method_qualifier = "private_" - method_anchor = f"class_{class_name}_{method_qualifier}method_{m.name}" + method_anchor = f"class_{sanitize_class_name(class_name)}_{method_qualifier}method_{m.name}" f.write(f".. _{method_anchor}:\n\n") self_link = f" :ref:`🔗<{method_anchor}>`" @@ -1459,7 +1436,9 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create operator signature and anchor point. - operator_anchor = f"class_{class_name}_operator_{sanitize_operator_name(m.name, state)}" + operator_anchor = ( + f"class_{sanitize_class_name(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") @@ -1501,7 +1480,9 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Create theme property signature and anchor point. - theme_item_anchor = f"class_{class_name}_theme_{theme_item_def.data_name}_{theme_item_def.name}" + theme_item_anchor = ( + f"class_{sanitize_class_name(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") @@ -1537,28 +1518,27 @@ def make_type(klass: str, state: State) -> str: if klass.find("*") != -1: # Pointer, ignore return f"``{klass}``" - link_type = klass - is_array = False + def resolve_type(link_type: str) -> str: + if link_type in state.classes: + return f":ref:`{link_type}`" + else: + print_error(f'{state.current_class}.xml: Unresolved type "{link_type}".', state) + return f"``{link_type}``" - if link_type.endswith("[]"): # Typed array, strip [] to link to contained type. - link_type = link_type[:-2] - is_array = True + if klass.endswith("[]"): # Typed array, strip [] to link to contained type. + return f":ref:`Array`\\[{resolve_type(klass[: -len('[]')])}\\]" - if link_type in state.classes: - type_rst = f":ref:`{link_type}`" - if is_array: - type_rst = f":ref:`Array`\\[{type_rst}\\]" - return type_rst + if klass.startswith("Dictionary["): # Typed dictionary, split elements to link contained types. + parts = klass[len("Dictionary[") : -len("]")].partition(", ") + key = parts[0] + value = parts[2] + return f":ref:`Dictionary`\\[{resolve_type(key)}, {resolve_type(value)}\\]" - 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`\\[{type_rst}\\]" - return type_rst + return resolve_type(klass) def make_enum(t: str, is_bitfield: bool, state: State) -> str: - p = t.find(".") + p = t.rfind(".") if p >= 0: c = t[0:p] e = t[p + 1 :] @@ -1576,13 +1556,11 @@ 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}`\\]" + return f"|bitfield|\\[:ref:`{e}`\\]" else: - return f":ref:`{e}`" + return f":ref:`{e}`" - # Don't fail for `Vector3.Axis`, as this enum is a special case which is expected not to be resolved. - if f"{c}.{e}" != "Vector3.Axis": - print_error(f'{state.current_class}.xml: Unresolved enum "{t}".', state) + print_error(f'{state.current_class}.xml: Unresolved enum "{t}".', state) return t @@ -1603,7 +1581,7 @@ def make_method_signature( if isinstance(definition, MethodDef) and ref_type != "": if ref_type == "operator": op_name = definition.name.replace("<", "\\<") # So operator "<" gets correctly displayed. - out += f":ref:`{op_name}`" + out += f":ref:`{definition.name}`" else: - out += f":ref:`{definition.name}`" + out += f":ref:`{definition.name}`" else: out += f"**{definition.name}**" @@ -1801,13 +1779,15 @@ def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_ f.write("\n") if group_name in CLASS_GROUPS_BASE: - f.write(f" class_{CLASS_GROUPS_BASE[group_name].lower()}\n") + f.write(f" class_{sanitize_class_name(CLASS_GROUPS_BASE[group_name], True)}\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(): + if group_name in CLASS_GROUPS_BASE and sanitize_class_name( + CLASS_GROUPS_BASE[group_name], True + ) == sanitize_class_name(class_name, True): continue - f.write(f" class_{class_name.lower()}\n") + f.write(f" class_{sanitize_class_name(class_name, True)}\n") f.write("\n") @@ -2288,7 +2268,9 @@ def format_text_block( repl_text = target_name if target_class_name != state.current_class: repl_text = f"{target_class_name}.{target_name}" - tag_text = f":ref:`{repl_text}`" + if tag_state.name == "method": + repl_text = f"{repl_text}()" + tag_text = f":ref:`{repl_text}`" escape_pre = True escape_post = True @@ -2592,7 +2574,7 @@ def format_table(f: TextIO, data: List[Tuple[Optional[str], ...]], remove_empty_ for i, text in enumerate(row): if column_sizes[i] == 0 and remove_empty_columns: continue - row_text += f' {(text or "").ljust(column_sizes[i])} |' + row_text += f" {(text or '').ljust(column_sizes[i])} |" row_text += "\n" f.write(f" {row_text}") @@ -2601,6 +2583,13 @@ def format_table(f: TextIO, data: List[Tuple[Optional[str], ...]], remove_empty_ f.write("\n") +def sanitize_class_name(dirty_name: str, is_file_name=False) -> str: + if is_file_name: + return dirty_name.lower().replace('"', "").replace("/", "--") + else: + return dirty_name.replace('"', "").replace("/", "_").replace(".", "_") + + def sanitize_operator_name(dirty_name: str, state: State) -> str: clear_name = dirty_name.replace("operator ", "") diff --git a/extension/doc_tools/misc/utility/color.py b/extension/doc_tools/misc/utility/color.py new file mode 100644 index 00000000..ae392dd8 --- /dev/null +++ b/extension/doc_tools/misc/utility/color.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import os +import sys +from enum import Enum +from typing import Final + +# Colors are disabled in non-TTY environments such as pipes. This means if output is redirected +# to a file, it won't contain color codes. Colors are always enabled on continuous integration. + +IS_CI: Final[bool] = bool(os.environ.get("CI")) +STDOUT_TTY: Final[bool] = bool(sys.stdout.isatty()) +STDERR_TTY: Final[bool] = bool(sys.stderr.isatty()) + + +def _color_supported(stdout: bool) -> bool: + """ + Validates if the current environment supports colored output. Attempts to enable ANSI escape + code support on Windows 10 and later. + """ + if IS_CI: + return True + + if sys.platform != "win32": + return STDOUT_TTY if stdout else STDERR_TTY + else: + from ctypes import POINTER, WINFUNCTYPE, WinError, windll + from ctypes.wintypes import BOOL, DWORD, HANDLE + + STD_HANDLE = -11 if stdout else -12 + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 + + def err_handler(result, func, args): + if not result: + raise WinError() + return args + + GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32), ((1, "nStdHandle"),)) + GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))( + ("GetConsoleMode", windll.kernel32), + ((1, "hConsoleHandle"), (2, "lpMode")), + ) + GetConsoleMode.errcheck = err_handler + SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)( + ("SetConsoleMode", windll.kernel32), + ((1, "hConsoleHandle"), (1, "dwMode")), + ) + SetConsoleMode.errcheck = err_handler + + try: + handle = GetStdHandle(STD_HANDLE) + flags = GetConsoleMode(handle) + SetConsoleMode(handle, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + return True + except OSError: + return False + + +STDOUT_COLOR: Final[bool] = _color_supported(True) +STDERR_COLOR: Final[bool] = _color_supported(False) +_stdout_override: bool = STDOUT_COLOR +_stderr_override: bool = STDERR_COLOR + + +def toggle_color(stdout: bool, value: bool | None = None) -> None: + """ + Explicitly toggle color codes, regardless of support. + + - `stdout`: A boolean to choose the output stream. `True` for stdout, `False` for stderr. + - `value`: An optional boolean to explicitly set the color state instead of toggling. + """ + if stdout: + global _stdout_override + _stdout_override = value if value is not None else not _stdout_override + else: + global _stderr_override + _stderr_override = value if value is not None else not _stderr_override + + +class Ansi(Enum): + """ + Enum class for adding ansi codepoints directly into strings. Automatically converts values to + strings representing their internal value. + """ + + RESET = "\x1b[0m" + + BOLD = "\x1b[1m" + DIM = "\x1b[2m" + ITALIC = "\x1b[3m" + UNDERLINE = "\x1b[4m" + STRIKETHROUGH = "\x1b[9m" + REGULAR = "\x1b[22;23;24;29m" + + BLACK = "\x1b[30m" + RED = "\x1b[31m" + GREEN = "\x1b[32m" + YELLOW = "\x1b[33m" + BLUE = "\x1b[34m" + MAGENTA = "\x1b[35m" + CYAN = "\x1b[36m" + WHITE = "\x1b[37m" + GRAY = "\x1b[90m" + + def __str__(self) -> str: + return self.value + + +def print_info(*values: object) -> None: + """Prints a informational message with formatting.""" + if _stdout_override: + print(f"{Ansi.GRAY}{Ansi.BOLD}INFO:{Ansi.REGULAR}", *values, Ansi.RESET) + else: + print(*values) + + +def print_warning(*values: object) -> None: + """Prints a warning message with formatting.""" + if _stderr_override: + print(f"{Ansi.YELLOW}{Ansi.BOLD}WARNING:{Ansi.REGULAR}", *values, Ansi.RESET, file=sys.stderr) + else: + print(*values, file=sys.stderr) + + +def print_error(*values: object) -> None: + """Prints an error message with formatting.""" + if _stderr_override: + print(f"{Ansi.RED}{Ansi.BOLD}ERROR:{Ansi.REGULAR}", *values, Ansi.RESET, file=sys.stderr) + else: + print(*values, file=sys.stderr) diff --git a/extension/doc_tools/patches/make_rst.patch b/extension/doc_tools/patches/make_rst.patch index d5a2216d..5e41f1d7 100644 --- a/extension/doc_tools/patches/make_rst.patch +++ b/extension/doc_tools/patches/make_rst.patch @@ -1,16 +1,16 @@ diff --git a/extension/doc_tools/make_rst.py b/extension/doc_tools/make_rst.py -index 761a7f8..5689df4 100644 +index 0b4896d..7c269f7 100644 --- a/extension/doc_tools/make_rst.py +++ b/extension/doc_tools/make_rst.py -@@ -13,7 +13,6 @@ 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 # noqa: E402 +@@ -12,7 +12,6 @@ from typing import Any, Dict, List, Optional, TextIO, Tuple, Union + + sys.path.insert(0, root_directory := os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")) + +-import version + from misc.utility.color import Ansi, toggle_color # $DOCS_URL/path/to/page.html(#fragment-tag) - GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") -@@ -674,6 +673,13 @@ class ScriptLanguageParityCheck: +@@ -672,6 +671,13 @@ class ScriptLanguageParityCheck: self.hit_map[class_name].append((context, error)) @@ -24,7 +24,7 @@ index 761a7f8..5689df4 100644 # Entry point for the RST generator. def main() -> None: parser = argparse.ArgumentParser() -@@ -744,21 +750,46 @@ def main() -> None: +@@ -719,21 +725,46 @@ def main() -> None: else: print(f'No PO file at "{lang_file}" for language "{args.lang}".') @@ -73,7 +73,7 @@ index 761a7f8..5689df4 100644 elif os.path.isdir(path): file_list += (os.path.join(path, f) for f in os.listdir(path) if f.endswith(".xml")) -@@ -806,6 +837,8 @@ def main() -> None: +@@ -781,6 +812,8 @@ def main() -> None: grouped_classes: Dict[str, List[str]] = {} for class_name, class_def in state.classes.items(): @@ -82,7 +82,7 @@ index 761a7f8..5689df4 100644 if args.filter and not pattern.search(class_def.filepath): continue state.current_class = class_name -@@ -899,9 +932,6 @@ def translate(string: str) -> str: +@@ -872,9 +905,6 @@ def translate(string: str) -> str: def get_git_branch() -> str: @@ -92,7 +92,7 @@ index 761a7f8..5689df4 100644 return "master" -@@ -928,11 +958,11 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: +@@ -901,11 +931,11 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: git_branch = get_git_branch() source_xml_path = os.path.relpath(class_def.filepath, root_directory).replace("\\", "/") @@ -107,7 +107,7 @@ index 761a7f8..5689df4 100644 f.write(f".. Generator: {generator_github_url}.\n") f.write(f".. XML source: {source_github_url}.\n\n") -@@ -1751,7 +1781,7 @@ def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_ +@@ -1729,7 +1759,7 @@ def make_rst_index(grouped_classes: Dict[str, List[str]], dry_run: bool, output_ # Also provide links to the source files for reference. git_branch = get_git_branch()