diff options
Diffstat (limited to 'doc/tools')
-rwxr-xr-x | doc/tools/doc_status.py | 32 | ||||
-rwxr-xr-x | doc/tools/make_rst.py | 295 |
2 files changed, 226 insertions, 101 deletions
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py index 629b5a032b..cc0733cab2 100755 --- a/doc/tools/doc_status.py +++ b/doc/tools/doc_status.py @@ -66,10 +66,23 @@ table_columns = [ "methods", "constants", "members", - "signals", "theme_items", + "signals", + "operators", + "constructors", +] +table_column_names = [ + "Name", + "Brief Desc.", + "Desc.", + "Methods", + "Constants", + "Members", + "Theme Items", + "Signals", + "Operators", + "Constructors", ] -table_column_names = ["Name", "Brief Desc.", "Desc.", "Methods", "Constants", "Members", "Signals", "Theme Items"] colors = { "name": [36], # cyan "part_big_problem": [4, 31], # underline, red @@ -82,7 +95,7 @@ colors = { "state_on": [1, 35], # bold, magenta/plum "bold": [1], # bold } -overall_progress_description_weigth = 10 +overall_progress_description_weight = 10 ################################################################################ @@ -180,6 +193,8 @@ class ClassStatus: "members": ClassStatusProgress(), "theme_items": ClassStatusProgress(), "signals": ClassStatusProgress(), + "operators": ClassStatusProgress(), + "constructors": ClassStatusProgress(), } def __add__(self, other): @@ -218,12 +233,12 @@ class ClassStatus: output["description"] = ok_string if self.has_description else missing_string description_progress = ClassStatusProgress( - (self.has_brief_description + self.has_description) * overall_progress_description_weigth, - 2 * overall_progress_description_weigth, + (self.has_brief_description + self.has_description) * overall_progress_description_weight, + 2 * overall_progress_description_weight, ) items_progress = ClassStatusProgress() - for k in ["methods", "constants", "members", "signals", "theme_items"]: + for k in ["methods", "constants", "members", "theme_items", "signals", "constructors", "operators"]: items_progress += self.progresses[k] output[k] = self.progresses[k].to_configured_colored_string() @@ -260,7 +275,7 @@ class ClassStatus: elif tag.tag == "description": status.has_description = len(tag.text.strip()) > 0 - elif tag.tag in ["methods", "signals"]: + elif tag.tag in ["methods", "signals", "operators", "constructors"]: for sub_tag in list(tag): descr = sub_tag.find("description") status.progresses[tag.tag].increment(len(descr.text.strip()) > 0) @@ -272,9 +287,6 @@ class ClassStatus: elif tag.tag in ["tutorials"]: pass # Ignore those tags for now - elif tag.tag in ["theme_items"]: - pass # Ignore those tags, since they seem to lack description at all - else: print(tag.tag, tag.attrib) diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index fe8083f218..eba4cee33a 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -11,15 +11,54 @@ from collections import OrderedDict # Uncomment to do type checks. I have it commented out so it works below Python 3.5 # from typing import List, Dict, TextIO, Tuple, Iterable, Optional, DefaultDict, Any, Union -# http(s)://docs.godotengine.org/<langcode>/<tag>/path/to/page.html(#fragment-tag) -GODOT_DOCS_PATTERN = re.compile( - r"^http(?:s)?://docs\.godotengine\.org/(?:[a-zA-Z0-9.\-_]*)/(?:[a-zA-Z0-9.\-_]*)/(.*)\.html(#.*)?$" -) +# $DOCS_URL/path/to/page.html(#fragment-tag) +GODOT_DOCS_PATTERN = re.compile(r"^\$DOCS_URL/(.*)\.html(#.*)?$") + +# Based on reStructedText inline markup recognition rules +# https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules +MARKUP_ALLOWED_PRECEDENT = " -:/'\"<([{" +MARKUP_ALLOWED_SUBSEQUENT = " -.,:;!?\\/'\")]}>" + +# Used to translate section headings and other hardcoded strings when required with +# the --lang argument. The BASE_STRINGS list should be synced with what we actually +# write in this script (check `translate()` uses), and also hardcoded in +# `doc/translations/extract.py` to include them in the source POT file. +BASE_STRINGS = [ + "Description", + "Tutorials", + "Properties", + "Constructors", + "Methods", + "Operators", + "Theme Properties", + "Signals", + "Enumerations", + "Constants", + "Property Descriptions", + "Constructor Descriptions", + "Method Descriptions", + "Operator Descriptions", + "Theme Property Descriptions", + "Inherits:", + "Inherited By:", + "(overrides %s)", + "Default", + "Setter", + "value", + "Getter", + "This method should typically be overridden by the user to have any effect.", + "This method has no side effects. It doesn't modify any of the instance's member variables.", + "This method accepts any number of arguments after the ones described here.", + "This method is used to construct a type.", + "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.", +] +strings_l10n = {} def print_error(error, state): # type: (str, State) -> None print("ERROR: {}".format(error)) - state.errored = True + state.num_errors += 1 class TypeName: @@ -42,15 +81,15 @@ class TypeName: class PropertyDef: def __init__( - self, name, type_name, setter, getter, text, default_value, overridden - ): # type: (str, TypeName, Optional[str], Optional[str], Optional[str], Optional[str], Optional[bool]) -> None + self, name, type_name, setter, getter, text, default_value, overrides + ): # type: (str, TypeName, Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> None self.name = name self.type_name = type_name self.setter = setter self.getter = getter self.text = text self.default_value = default_value - self.overridden = overridden + self.overrides = overrides class ParameterDef: @@ -124,8 +163,7 @@ class ClassDef: class State: def __init__(self): # type: () -> None - # Has any error been reported? - self.errored = False + self.num_errors = 0 self.classes = OrderedDict() # type: OrderedDict[str, ClassDef] self.current_class = "" # type: str @@ -155,7 +193,7 @@ class State: property_name = property.attrib["name"] if property_name in class_def.properties: - print_error("Duplicate property '{}', file: {}".format(property_name, class_name), self) + print_error('{}.xml: Duplicate property "{}".'.format(class_name, property_name), self) continue type_name = TypeName.from_element(property) @@ -164,10 +202,10 @@ class State: default_value = property.get("default") or None if default_value is not None: default_value = "``{}``".format(default_value) - overridden = property.get("override") or False + overrides = property.get("overrides") or None property_def = PropertyDef( - property_name, type_name, setter, getter, property.text, default_value, overridden + property_name, type_name, setter, getter, property.text, default_value, overrides ) class_def.properties[property_name] = property_def @@ -266,7 +304,7 @@ class State: constant_def = ConstantDef(constant_name, value, constant.text) if enum is None: if constant_name in class_def.constants: - print_error("Duplicate constant '{}', file: {}".format(constant_name, class_name), self) + print_error('{}.xml: Duplicate constant "{}".'.format(class_name, constant_name), self) continue class_def.constants[constant_name] = constant_def @@ -289,7 +327,7 @@ class State: signal_name = signal.attrib["name"] if signal_name in class_def.signals: - print_error("Duplicate signal '{}', file: {}".format(signal_name, class_name), self) + print_error('{}.xml: Duplicate signal "{}".'.format(class_name, signal_name), self) continue params = parse_arguments(signal) @@ -312,8 +350,8 @@ class State: theme_item_id = "{}_{}".format(theme_item_data_name, theme_item_name) if theme_item_id in class_def.theme_items: print_error( - "Duplicate theme property '{}' of type '{}', file: {}".format( - theme_item_name, theme_item_data_name, class_name + '{}.xml: Duplicate theme item "{}" of type "{}".'.format( + class_name, theme_item_name, theme_item_data_name ), self, ) @@ -330,7 +368,7 @@ class State: theme_item.text, default_value, ) - class_def.theme_items[theme_item_id] = theme_item_def + class_def.theme_items[theme_item_name] = theme_item_def tutorials = class_root.find("tutorials") if tutorials is not None: @@ -364,6 +402,7 @@ def main(): # type: () -> None 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.") + parser.add_argument("--lang", "-l", default="en", help="Language to use for section headings.") group = parser.add_mutually_exclusive_group() group.add_argument("--output", "-o", default=".", help="The directory to save output .rst files in.") group.add_argument( @@ -373,13 +412,32 @@ def main(): # type: () -> None ) args = parser.parse_args() + # Retrieve heading translations for the given language. + if not args.dry_run and args.lang != "en": + lang_file = os.path.join( + os.path.dirname(os.path.realpath(__file__)), "..", "translations", "{}.po".format(args.lang) + ) + if os.path.exists(lang_file): + try: + import polib + except ImportError: + print("Base template strings localization requires `polib`.") + exit(1) + + pofile = polib.pofile(lang_file) + for entry in pofile.translated_entries(): + if entry.msgid in BASE_STRINGS: + strings_l10n[entry.msgid] = entry.msgstr + else: + print('No PO file at "{}" for language "{}".'.format(lang_file, args.lang)) + print("Checking for errors in the XML class reference...") file_list = [] # type: List[str] for path in args.path: # Cut off trailing slashes so os.path.basename doesn't choke. - if path.endswith(os.sep): + if path.endswith("/") or path.endswith("\\"): path = path[:-1] if os.path.basename(path) == "modules": @@ -394,7 +452,7 @@ def main(): # type: () -> None elif os.path.isfile(path): if not path.endswith(".xml"): - print("Got non-.xml file '{}' in input, skipping.".format(path)) + print('Got non-.xml file "{}" in input, skipping.'.format(path)) continue file_list.append(path) @@ -406,17 +464,17 @@ def main(): # type: () -> None try: tree = ET.parse(cur_file) except ET.ParseError as e: - print_error("Parse error reading file '{}': {}".format(cur_file, e), state) + print_error("{}.xml: Parse error while reading the file: {}".format(cur_file, e), state) continue doc = tree.getroot() if "version" not in doc.attrib: - print_error("Version missing from 'doc', file: {}".format(cur_file), state) + print_error('{}.xml: "version" attribute missing from "doc".'.format(cur_file), state) continue name = doc.attrib["name"] if name in classes: - print_error("Duplicate class '{}'".format(name), state) + print_error('{}.xml: Duplicate class "{}".'.format(cur_file, name), state) continue classes[name] = (doc, cur_file) @@ -425,7 +483,7 @@ def main(): # type: () -> None try: state.parse_class(data[0], data[1]) except Exception as e: - print_error("Exception while parsing class '{}': {}".format(name, e), state) + print_error("{}.xml: Exception while parsing class: {}".format(name, e), state) state.sort_classes() @@ -440,15 +498,28 @@ def main(): # type: () -> None state.current_class = class_name make_rst_class(class_def, state, args.dry_run, args.output) - if not state.errored: - print("No errors found.") + if state.num_errors == 0: + print("No errors found in the class reference XML.") if not args.dry_run: print("Wrote reStructuredText files for each class to: %s" % args.output) else: - print("Errors were found in the class reference XML. Please check the messages above.") + if state.num_errors >= 2: + print( + "%d errors were found in the class reference XML. Please check the messages above." % state.num_errors + ) + else: + print("1 error was found in the class reference XML. Please check the messages above.") exit(1) +def translate(string): # type: (str) -> str + """Translate a string based on translations sourced from `doc/translations/*.po` + for a language if defined via the --lang command line argument. + Returns the original string if no translation exists. + """ + return strings_l10n.get(string, string) + + def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, State, bool, str) -> None class_name = class_def.name @@ -464,24 +535,24 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S f.write(".. The source is found in doc/classes or modules/<name>/doc_classes.\n\n") f.write(".. _class_" + class_name + ":\n\n") - f.write(make_heading(class_name, "=")) + f.write(make_heading(class_name, "=", False)) # Inheritance tree # Ascendants if class_def.inherits: - inh = class_def.inherits.strip() - f.write("**Inherits:** ") + inherits = class_def.inherits.strip() + f.write("**" + translate("Inherits:") + "** ") first = True - while inh in state.classes: + while inherits in state.classes: if not first: f.write(" **<** ") else: first = False - f.write(make_type(inh, state)) - inode = state.classes[inh].inherits + f.write(make_type(inherits, state)) + inode = state.classes[inherits].inherits if inode: - inh = inode.strip() + inherits = inode.strip() else: break f.write("\n\n") @@ -493,7 +564,7 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S inherited.append(c.name) if len(inherited): - f.write("**Inherited By:** ") + f.write("**" + translate("Inherited By:") + "** ") for i, child in enumerate(inherited): if i > 0: f.write(", ") @@ -522,8 +593,10 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S 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.overridden: - ml.append((type_rst, property_def.name, default + " *(parent override)*")) + if default is not None and property_def.overrides: + ref = ":ref:`{1}<class_{1}_property_{0}>`".format(property_def.name, property_def.overrides) + # Not using translate() for now as it breaks table formatting. + ml.append((type_rst, property_def.name, default + " " + "(overrides %s)" % ref)) else: ref = ":ref:`{0}<class_{1}_property_{0}>`".format(property_def.name, class_name) ml.append((type_rst, ref, default)) @@ -626,12 +699,12 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S f.write("\n\n") # Property descriptions - if any(not p.overridden for p in class_def.properties.values()) > 0: + if any(not p.overrides for p in class_def.properties.values()) > 0: f.write(make_heading("Property Descriptions", "-")) index = 0 for property_def in class_def.properties.values(): - if property_def.overridden: + if property_def.overrides: continue if index != 0: @@ -641,12 +714,13 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S f.write("- {} **{}**\n\n".format(property_def.type_name.to_rst(state), property_def.name)) info = [] + # Not using translate() for now as it breaks table formatting. if property_def.default_value is not None: - info.append(("*Default*", property_def.default_value)) + info.append(("*" + "Default" + "*", property_def.default_value)) if property_def.setter is not None and not property_def.setter.startswith("_"): - info.append(("*Setter*", property_def.setter + "(value)")) + info.append(("*" + "Setter" + "*", property_def.setter + "(" + "value" + ")")) if property_def.getter is not None and not property_def.getter.startswith("_"): - info.append(("*Getter*", property_def.getter + "()")) + info.append(("*" + "Getter" + "*", property_def.getter + "()")) if len(info) > 0: format_table(f, info) @@ -735,7 +809,8 @@ def make_rst_class(class_def, state, dry_run, output_dir): # type: (ClassDef, S info = [] if theme_item_def.default_value is not None: - info.append(("*Default*", theme_item_def.default_value)) + # Not using translate() for now as it breaks table formatting. + info.append(("*" + "Default" + "*", theme_item_def.default_value)) if len(info) > 0: format_table(f, info) @@ -785,7 +860,7 @@ def escape_rst(text, until_pos=-1): # type: (str) -> str def format_codeblock(code_type, post_text, indent_level, state): # types: str, str, int, state end_pos = post_text.find("[/" + code_type + "]") if end_pos == -1: - print_error("[" + code_type + "] without a closing tag, file: {}".format(state.current_class), state) + print_error("{}.xml: [" + code_type + "] without a closing tag.".format(state.current_class), state) return None code_text = post_text[len("[" + code_type + "]") : end_pos] @@ -804,9 +879,9 @@ def format_codeblock(code_type, post_text, indent_level, state): # types: str, if to_skip > indent_level: print_error( - "Four spaces should be used for indentation within [" + "{}.xml: Four spaces should be used for indentation within [" + code_type - + "], file: {}".format(state.current_class), + + "].".format(state.current_class), state, ) @@ -845,28 +920,23 @@ def rstize_text(text, state): # type: (str, State) -> str if result is None: return "" text = pre_text + result[0] - pos += result[1] + pos += result[1] - indent_level # Handle normal text else: text = pre_text + "\n\n" + post_text - pos += 2 + pos += 2 - indent_level next_brac_pos = text.find("[") text = escape_rst(text, next_brac_pos) # Handle [tags] inside_code = False - inside_url = False - url_has_name = False - url_link = "" pos = 0 tag_depth = 0 previous_pos = 0 while True: pos = text.find("[", pos) - if inside_url and (pos > previous_pos): - url_has_name = True if pos == -1: break @@ -878,6 +948,7 @@ def rstize_text(text, state): # type: (str, State) -> str post_text = text[endq_pos + 1 :] tag_text = text[pos + 1 : endq_pos] + escape_pre = False escape_post = False if tag_text in state.classes: @@ -886,6 +957,7 @@ def rstize_text(text, state): # type: (str, State) -> str tag_text = "``{}``".format(tag_text) else: tag_text = make_type(tag_text, state) + escape_pre = True escape_post = True else: # command cmd = tag_text @@ -912,13 +984,14 @@ def rstize_text(text, state): # type: (str, State) -> str or cmd.startswith("member") or cmd.startswith("signal") or cmd.startswith("constant") + or cmd.startswith("theme_item") ): param = tag_text[space_pos + 1 :] if param.find(".") != -1: ss = param.split(".") if len(ss) > 2: - print_error("Bad reference: '{}', file: {}".format(param, state.current_class), state) + print_error('{}.xml: Bad reference: "{}".'.format(state.current_class, param), state) class_param, method_param = ss else: @@ -931,26 +1004,31 @@ def rstize_text(text, state): # type: (str, State) -> str if cmd.startswith("constructor"): if method_param not in class_def.constructors: print_error( - "Unresolved constructor '{}', file: {}".format(param, state.current_class), state + '{}.xml: Unresolved constructor "{}".'.format(state.current_class, param), state ) ref_type = "_constructor" if cmd.startswith("method"): if method_param not in class_def.methods: - print_error("Unresolved method '{}', file: {}".format(param, state.current_class), state) + print_error('{}.xml: Unresolved method "{}".'.format(state.current_class, param), state) ref_type = "_method" if cmd.startswith("operator"): if method_param not in class_def.operators: - print_error("Unresolved operator '{}', file: {}".format(param, state.current_class), state) + print_error('{}.xml: Unresolved operator "{}".'.format(state.current_class, param), state) ref_type = "_operator" elif cmd.startswith("member"): if method_param not in class_def.properties: - print_error("Unresolved member '{}', file: {}".format(param, state.current_class), state) + print_error('{}.xml: Unresolved member "{}".'.format(state.current_class, param), state) ref_type = "_property" + elif cmd.startswith("theme_item"): + if method_param not in class_def.theme_items: + print_error('{}.xml: Unresolved theme item "{}".'.format(state.current_class, param), state) + ref_type = "_theme_{}".format(class_def.theme_items[method_param].data_name) + elif cmd.startswith("signal"): if method_param not in class_def.signals: - print_error("Unresolved signal '{}', file: {}".format(param, state.current_class), state) + print_error('{}.xml: Unresolved signal "{}".'.format(state.current_class, param), state) ref_type = "_signal" elif cmd.startswith("constant"): @@ -976,13 +1054,13 @@ def rstize_text(text, state): # type: (str, State) -> str break if not found: - print_error("Unresolved constant '{}', file: {}".format(param, state.current_class), state) + print_error('{}.xml: Unresolved constant "{}".'.format(state.current_class, param), state) ref_type = "_constant" else: print_error( - "Unresolved type reference '{}' in method reference '{}', file: {}".format( - class_param, param, state.current_class + '{}.xml: Unresolved type reference "{}" in method reference "{}".'.format( + state.current_class, class_param, param ), state, ) @@ -991,21 +1069,35 @@ def rstize_text(text, state): # type: (str, State) -> str if class_param != state.current_class: repl_text = "{}.{}".format(class_param, method_param) tag_text = ":ref:`{}<class_{}{}_{}>`".format(repl_text, class_param, ref_type, method_param) + escape_pre = True escape_post = True elif cmd.find("image=") == 0: tag_text = "" # '![](' + cmd[6:] + ')' elif cmd.find("url=") == 0: - url_link = cmd[4:] - tag_text = "`" - tag_depth += 1 - inside_url = True - url_has_name = False - elif cmd == "/url": - tag_text = ("" if url_has_name else url_link) + " <" + url_link + ">`__" - tag_depth -= 1 - escape_post = True - inside_url = False - url_has_name = False + # URLs are handled in full here as we need to extract the optional link + # title to use `make_link`. + link_url = cmd[4:] + endurl_pos = text.find("[/url]", endq_pos + 1) + if endurl_pos == -1: + print_error( + "{}.xml: Tag depth mismatch for [url]: no closing [/url]".format(state.current_class), state + ) + break + link_title = text[endq_pos + 1 : endurl_pos] + tag_text = make_link(link_url, link_title) + + pre_text = text[:pos] + post_text = text[endurl_pos + 6 :] + + if pre_text and pre_text[-1] not in MARKUP_ALLOWED_PRECEDENT: + pre_text += "\ " + if post_text and post_text[0] not in MARKUP_ALLOWED_SUBSEQUENT: + post_text = "\ " + post_text + + text = pre_text + tag_text + post_text + pos = len(pre_text) + len(tag_text) + previous_pos = pos + continue elif cmd == "center": tag_depth += 1 tag_text = "" @@ -1039,40 +1131,53 @@ def rstize_text(text, state): # type: (str, State) -> str elif cmd == "i" or cmd == "/i": if cmd == "/i": tag_depth -= 1 + escape_post = True else: tag_depth += 1 + escape_pre = True tag_text = "*" elif cmd == "b" or cmd == "/b": if cmd == "/b": tag_depth -= 1 + escape_post = True else: tag_depth += 1 + escape_pre = True tag_text = "**" elif cmd == "u" or cmd == "/u": if cmd == "/u": tag_depth -= 1 + escape_post = True else: tag_depth += 1 + escape_pre = True tag_text = "" elif cmd == "code": tag_text = "``" tag_depth += 1 inside_code = True + escape_pre = True elif cmd == "kbd": tag_text = ":kbd:`" tag_depth += 1 + escape_pre = True elif cmd == "/kbd": tag_text = "`" tag_depth -= 1 + escape_post = True elif cmd.startswith("enum "): tag_text = make_enum(cmd[5:], state) + escape_pre = True escape_post = True else: tag_text = make_type(tag_text, state) + escape_pre = True escape_post = True # Properly escape things like `[Node]s` - if escape_post and post_text and (post_text[0].isalnum() or post_text[0] == "("): # not punctuation, escape + if escape_pre and pre_text and pre_text[-1] not in MARKUP_ALLOWED_PRECEDENT: + pre_text += "\ " + if escape_post and post_text and post_text[0] not in MARKUP_ALLOWED_SUBSEQUENT: post_text = "\ " + post_text next_brac_pos = post_text.find("[", 0) @@ -1100,7 +1205,9 @@ def rstize_text(text, state): # type: (str, State) -> str previous_pos = pos if tag_depth > 0: - print_error("Tag depth mismatch: too many/little open/close tags, file: {}".format(state.current_class), state) + print_error( + "{}.xml: Tag depth mismatch: too many (or too little) open/close tags.".format(state.current_class), state + ) return text @@ -1144,7 +1251,7 @@ def make_type(klass, state): # type: (str, State) -> str link_type = link_type[:-2] if link_type in state.classes: return ":ref:`{}<class_{}>`".format(klass, link_type) - print_error("Unresolved type '{}', file: {}".format(klass, state.current_class), state) + print_error('{}.xml: Unresolved type "{}".'.format(state.current_class, klass), state) return klass @@ -1168,7 +1275,7 @@ def make_enum(t, state): # type: (str, State) -> str # Don't fail for `Vector3.Axis`, as this enum is a special case which is expected not to be resolved. if "{}.{}".format(c, e) != "Vector3.Axis": - print_error("Unresolved enum '{}', file: {}".format(t, state.current_class), state) + print_error('{}.xml: Unresolved enum "{}".'.format(state.current_class, t), state) return t @@ -1226,7 +1333,12 @@ def make_method_signature( return ret_type, out -def make_heading(title, underline): # type: (str, str) -> str +def make_heading(title, underline, l10n=True): # type: (str, str, bool) -> str + if l10n: + new_title = translate(title) + if new_title != title: + title = new_title + underline *= 2 # Double length to handle wide chars. return title + "\n" + (underline * len(title)) + "\n\n" @@ -1235,12 +1347,12 @@ def make_footer(): # type: () -> str # This way, we avoid bloating the generated rST with duplicate abbreviations. # fmt: off return ( - ".. |virtual| replace:: :abbr:`virtual (This method should typically be overridden by the user to have any effect.)`\n" - ".. |const| replace:: :abbr:`const (This method has no side effects. It doesn't modify any of the instance's member variables.)`\n" - ".. |vararg| replace:: :abbr:`vararg (This method accepts any number of arguments after the ones described here.)`\n" - ".. |constructor| replace:: :abbr:`constructor (This method is used to construct a type.)`\n" - ".. |static| replace:: :abbr:`static (This method doesn't need an instance to be called, so it can be called directly using the class name.)`\n" - ".. |operator| replace:: :abbr:`operator (This method describes a valid operator to use with this type as left-hand operand.)`\n" + ".. |virtual| replace:: :abbr:`virtual (" + translate("This method should typically be overridden by the user to have any effect.") + ")`\n" + ".. |const| replace:: :abbr:`const (" + translate("This method has no side effects. It doesn't modify any of the instance's member variables.") + ")`\n" + ".. |vararg| replace:: :abbr:`vararg (" + translate("This method accepts any number of arguments after the ones described here.") + ")`\n" + ".. |constructor| replace:: :abbr:`constructor (" + translate("This method is used to construct a type.") + ")`\n" + ".. |static| replace:: :abbr:`static (" + translate("This method doesn't need an instance to be called, so it can be called directly using the class name.") + ")`\n" + ".. |operator| replace:: :abbr:`operator (" + translate("This method describes a valid operator to use with this type as left-hand operand.") + ")`\n" ) # fmt: on @@ -1252,21 +1364,22 @@ def make_link(url, title): # type: (str, str) -> str if match.lastindex == 2: # Doc reference with fragment identifier: emit direct link to section with reference to page, for example: # `#calling-javascript-from-script in Exporting For Web` - return "`" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`_ in :doc:`../" + groups[0] + "`" - # Commented out alternative: Instead just emit: - # `Subsection in Exporting For Web` - # return "`Subsection <../" + groups[0] + ".html" + groups[1] + ">`__ in :doc:`../" + groups[0] + "`" + # Or use the title if provided. + if title != "": + return "`" + title + " <../" + groups[0] + ".html" + groups[1] + ">`__" + return "`" + groups[1] + " <../" + groups[0] + ".html" + groups[1] + ">`__ in :doc:`../" + groups[0] + "`" elif match.lastindex == 1: # Doc reference, for example: # `Math` + if title != "": + return ":doc:`" + title + " <../" + groups[0] + ">`" return ":doc:`../" + groups[0] + "`" else: # External link, for example: # `http://enet.bespin.org/usergroup0.html` if title != "": return "`" + title + " <" + url + ">`__" - else: - return "`" + url + " <" + url + ">`__" + return "`" + url + " <" + url + ">`__" def sanitize_operator_name(dirty_name, state): # type: (str, State) -> str @@ -1320,7 +1433,7 @@ def sanitize_operator_name(dirty_name, state): # type: (str, State) -> str else: clear_name = "xxx" - print_error("Unsupported operator type '{}', please add the missing rule.".format(dirty_name), state) + print_error('Unsupported operator type "{}", please add the missing rule.'.format(dirty_name), state) return clear_name |