summaryrefslogtreecommitdiffstats
path: root/doc/tools/make_rst.py
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tools/make_rst.py')
-rwxr-xr-xdoc/tools/make_rst.py213
1 files changed, 154 insertions, 59 deletions
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index c3a21f3d7b..4435d52527 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -68,6 +68,8 @@ BASE_STRINGS = [
"This value is an integer composed as a bitmask of the following flags.",
"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 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>`!",
"There is currently no description for this constructor. Please help us by :ref:`contributing one <doc_updating_the_class_reference>`!",
@@ -105,6 +107,7 @@ EDITOR_CLASSES: List[str] = [
CLASSES_WITH_CSHARP_DIFFERENCES: List[str] = [
"@GlobalScope",
"String",
+ "StringName",
"NodePath",
"Signal",
"Callable",
@@ -140,6 +143,9 @@ class State:
self.classes: OrderedDict[str, ClassDef] = OrderedDict()
self.current_class: str = ""
+ # Additional content and structure checks and validators.
+ self.script_language_parity_check: ScriptLanguageParityCheck = ScriptLanguageParityCheck()
+
def parse_class(self, class_root: ET.Element, filepath: str) -> None:
class_name = class_root.attrib["name"]
self.current_class = class_name
@@ -542,6 +548,9 @@ class ClassDef(DefinitionBase):
def __init__(self, name: str) -> None:
super().__init__("class", name)
+ self.class_group = "variant"
+ self.editor_class = self._is_editor_class()
+
self.constants: OrderedDict[str, ConstantDef] = OrderedDict()
self.enums: OrderedDict[str, EnumDef] = OrderedDict()
self.properties: OrderedDict[str, PropertyDef] = OrderedDict()
@@ -559,6 +568,65 @@ class ClassDef(DefinitionBase):
# Used to match the class with XML source for output filtering purposes.
self.filepath: str = ""
+ def _is_editor_class(self) -> bool:
+ if self.name.startswith("Editor"):
+ return True
+ if self.name in EDITOR_CLASSES:
+ return True
+
+ return False
+
+ def update_class_group(self, state: State) -> None:
+ group_name = "variant"
+
+ if self.name.startswith("@"):
+ group_name = "global"
+ elif self.inherits:
+ inherits = self.inherits.strip()
+
+ while inherits in state.classes:
+ if inherits == "Node":
+ group_name = "node"
+ break
+ if inherits == "Resource":
+ group_name = "resource"
+ break
+ if inherits == "Object":
+ group_name = "object"
+ break
+
+ inode = state.classes[inherits].inherits
+ if inode:
+ inherits = inode.strip()
+ else:
+ break
+
+ self.class_group = group_name
+
+
+# Checks if code samples have both GDScript and C# variations.
+# For simplicity we assume that a GDScript example is always present, and ignore contexts
+# which don't necessarily need C# examples.
+class ScriptLanguageParityCheck:
+ def __init__(self) -> None:
+ self.hit_map: OrderedDict[str, List[Tuple[DefinitionBase, str]]] = OrderedDict()
+ self.hit_count = 0
+
+ def add_hit(self, class_name: str, context: DefinitionBase, error: str, state: State) -> None:
+ if class_name in ["@GDScript", "@GlobalScope"]:
+ return # We don't expect these contexts to have parity.
+
+ class_def = state.classes[class_name]
+ if class_def.class_group == "variant" and class_def.name != "Object":
+ return # Variant types are replaced with native types in C#, we don't expect parity.
+
+ self.hit_count += 1
+
+ if class_name not in self.hit_map:
+ self.hit_map[class_name] = []
+
+ self.hit_map[class_name].append((context, error))
+
# Entry point for the RST generator.
def main() -> None:
@@ -589,6 +657,11 @@ def main() -> None:
action="store_true",
help="If passed, no output will be generated and XML files are only checked for errors.",
)
+ parser.add_argument(
+ "--verbose",
+ action="store_true",
+ help="If passed, enables verbose printing.",
+ )
args = parser.parse_args()
should_color = args.color or (hasattr(sys.stdout, "isatty") and sys.stdout.isatty())
@@ -683,15 +756,15 @@ def main() -> None:
if args.filter and not pattern.search(class_def.filepath):
continue
state.current_class = class_name
- make_rst_class(class_def, state, args.dry_run, args.output)
- group_name = get_class_group(class_def, state)
+ class_def.update_class_group(state)
+ make_rst_class(class_def, state, args.dry_run, args.output)
- if group_name not in grouped_classes:
- grouped_classes[group_name] = []
- grouped_classes[group_name].append(class_name)
+ if class_def.class_group not in grouped_classes:
+ grouped_classes[class_def.class_group] = []
+ grouped_classes[class_def.class_group].append(class_name)
- if is_editor_class(class_def):
+ if class_def.editor_class:
if "editor" not in grouped_classes:
grouped_classes["editor"] = []
grouped_classes["editor"].append(class_name)
@@ -703,6 +776,26 @@ def main() -> None:
print("")
+ # Print out checks.
+
+ 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"]}'
+ )
+ else:
+ print(
+ f'{STYLES["yellow"]}{state.script_language_parity_check.hit_count} code samples failed parity check:{STYLES["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"]}')
+
+ for context, error in class_hits:
+ print(f" - {error} in {format_context_name(context)}")
+ print("")
+
# Print out warnings and errors, or lack thereof, and exit with an appropriate code.
if state.num_warnings >= 2:
@@ -759,46 +852,6 @@ def get_git_branch() -> str:
return "master"
-def get_class_group(class_def: ClassDef, state: State) -> str:
- group_name = "variant"
- class_name = class_def.name
-
- if class_name.startswith("@"):
- group_name = "global"
- elif class_def.inherits:
- inherits = class_def.inherits.strip()
-
- while inherits in state.classes:
- if inherits == "Node":
- group_name = "node"
- break
- if inherits == "Resource":
- group_name = "resource"
- break
- if inherits == "Object":
- group_name = "object"
- break
-
- inode = state.classes[inherits].inherits
- if inode:
- inherits = inode.strip()
- else:
- break
-
- return group_name
-
-
-def is_editor_class(class_def: ClassDef) -> bool:
- class_name = class_def.name
-
- if class_name.startswith("Editor"):
- return True
- if class_name in EDITOR_CLASSES:
- return True
-
- return False
-
-
# Generator methods.
@@ -1051,6 +1104,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
if value.text is not None and value.text.strip() != "":
f.write(f"{format_text_block(value.text.strip(), value, state)}")
+ else:
+ 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")
@@ -1074,6 +1135,14 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir:
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"
+ )
f.write("\n\n")
@@ -1641,7 +1710,7 @@ def parse_link_target(link_target: str, state: State, context_name: str) -> List
def format_text_block(
text: str,
- context: Union[DefinitionBase, None],
+ context: DefinitionBase,
state: State,
) -> str:
# Linebreak + tabs in the XML should become two line breaks unless in a "codeblock"
@@ -1690,6 +1759,10 @@ def format_text_block(
inside_code_tag = ""
inside_code_tabs = False
ignore_code_warnings = False
+ code_warning_if_intended_string = "If this is intended, use [code skip-lint]...[/code]."
+
+ has_codeblocks_gdscript = False
+ has_codeblocks_csharp = False
pos = 0
tag_depth = 0
@@ -1748,7 +1821,7 @@ def format_text_block(
else:
if not ignore_code_warnings and tag_state.closing:
print_warning(
- f'{state.current_class}.xml: Found a code string that looks like a closing tag "[{tag_state.raw}]" in {context_name}.',
+ f'{state.current_class}.xml: Found a code string that looks like a closing tag "[{tag_state.raw}]" in {context_name}. {code_warning_if_intended_string}',
state,
)
@@ -1758,6 +1831,17 @@ def format_text_block(
elif tag_state.name == "codeblocks":
if tag_state.closing:
+ if not has_codeblocks_gdscript or not has_codeblocks_csharp:
+ state.script_language_parity_check.add_hit(
+ state.current_class,
+ context,
+ "Only one script language sample found in [codeblocks]",
+ state,
+ )
+
+ has_codeblocks_gdscript = False
+ has_codeblocks_csharp = False
+
tag_depth -= 1
tag_text = ""
inside_code_tabs = False
@@ -1775,6 +1859,8 @@ def format_text_block(
f"{state.current_class}.xml: GDScript code block is used outside of [codeblocks] in {context_name}.",
state,
)
+ else:
+ has_codeblocks_gdscript = True
tag_text = "\n .. code-tab:: gdscript\n"
elif tag_state.name == "csharp":
if not inside_code_tabs:
@@ -1782,8 +1868,17 @@ def format_text_block(
f"{state.current_class}.xml: C# code block is used outside of [codeblocks] in {context_name}.",
state,
)
+ else:
+ has_codeblocks_csharp = True
tag_text = "\n .. code-tab:: csharp\n"
else:
+ state.script_language_parity_check.add_hit(
+ state.current_class,
+ context,
+ "Code sample is formatted with [codeblock] where [codeblocks] should be used",
+ state,
+ )
+
tag_text = "\n::\n"
inside_code = True
@@ -1815,7 +1910,7 @@ def format_text_block(
if inside_code_text in state.classes:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches one of the known classes in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches one of the known classes in {context_name}. {code_warning_if_intended_string}',
state,
)
@@ -1825,49 +1920,49 @@ def format_text_block(
if target_name in class_def.methods:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} method in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} method in {context_name}. {code_warning_if_intended_string}',
state,
)
elif target_name in class_def.constructors:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} constructor in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} constructor in {context_name}. {code_warning_if_intended_string}',
state,
)
elif target_name in class_def.operators:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} operator in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} operator in {context_name}. {code_warning_if_intended_string}',
state,
)
elif target_name in class_def.properties:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} member in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} member in {context_name}. {code_warning_if_intended_string}',
state,
)
elif target_name in class_def.signals:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} signal in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} signal in {context_name}. {code_warning_if_intended_string}',
state,
)
elif target_name in class_def.annotations:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} annotation in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} annotation in {context_name}. {code_warning_if_intended_string}',
state,
)
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}.',
+ 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}',
state,
)
elif target_name in class_def.constants:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} constant in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} constant in {context_name}. {code_warning_if_intended_string}',
state,
)
@@ -1875,7 +1970,7 @@ def format_text_block(
for enum in class_def.enums.values():
if target_name in enum.values:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} enum value in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches the {target_class_name}.{target_name} enum value in {context_name}. {code_warning_if_intended_string}',
state,
)
break
@@ -1886,7 +1981,7 @@ def format_text_block(
for param_def in context_params:
if param_def.name == inside_code_text:
print_warning(
- f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches one of the parameters in {context_name}.',
+ f'{state.current_class}.xml: Found a code string "{inside_code_text}" that matches one of the parameters in {context_name}. {code_warning_if_intended_string}',
state,
)
break