diff options
author | Yuri Sizov <yuris@humnom.net> | 2022-08-06 20:48:54 +0300 |
---|---|---|
committer | Yuri Sizov <yuris@humnom.net> | 2022-08-08 22:18:38 +0300 |
commit | 35c1eae8d70eb6ae49495339b95f89bcd084c3f2 (patch) | |
tree | 5058f5211a4f3d2b56d157dfd49e482299c64b9c | |
parent | 9b19a02f319c73762b5a25f91a44256e764f8d8e (diff) |
Add support for [param foo] syntax in valid documentation contexts
-rwxr-xr-x | doc/tools/make_rst.py | 105 | ||||
-rw-r--r-- | editor/editor_help.cpp | 15 |
2 files changed, 106 insertions, 14 deletions
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py index cf87990a11..a69c459743 100755 --- a/doc/tools/make_rst.py +++ b/doc/tools/make_rst.py @@ -135,6 +135,7 @@ class State: method_desc = desc_element.text method_def = MethodDef(method_name, return_type, params, method_desc, qualifiers) + method_def.definition_name = "constructor" if method_name not in class_def.constructors: class_def.constructors[method_name] = [] @@ -191,6 +192,7 @@ class State: method_desc = desc_element.text method_def = MethodDef(method_name, return_type, params, method_desc, qualifiers) + method_def.definition_name = "operator" if method_name not in class_def.operators: class_def.operators[method_name] = [] @@ -362,6 +364,8 @@ class PropertyDef: default_value: Optional[str], overrides: Optional[str], ) -> None: + self.definition_name = "property" + self.name = name self.type_name = type_name self.setter = setter @@ -373,6 +377,8 @@ class PropertyDef: class ParameterDef: def __init__(self, name: str, type_name: TypeName, default_value: Optional[str]) -> None: + self.definition_name = "parameter" + self.name = name self.type_name = type_name self.default_value = default_value @@ -380,6 +386,8 @@ class ParameterDef: class SignalDef: def __init__(self, name: str, parameters: List[ParameterDef], description: Optional[str]) -> None: + self.definition_name = "signal" + self.name = name self.parameters = parameters self.description = description @@ -393,6 +401,8 @@ class AnnotationDef: description: Optional[str], qualifiers: Optional[str], ) -> None: + self.definition_name = "annotation" + self.name = name self.parameters = parameters self.description = description @@ -408,6 +418,8 @@ class MethodDef: description: Optional[str], qualifiers: Optional[str], ) -> None: + self.definition_name = "method" + self.name = name self.return_type = return_type self.parameters = parameters @@ -417,6 +429,8 @@ class MethodDef: class ConstantDef: def __init__(self, name: str, value: str, text: Optional[str], bitfield: bool) -> None: + self.definition_name = "constant" + self.name = name self.value = value self.text = text @@ -425,6 +439,8 @@ class ConstantDef: class EnumDef: def __init__(self, name: str, bitfield: bool) -> None: + self.definition_name = "enum" + self.name = name self.values: OrderedDict[str, ConstantDef] = OrderedDict() self.is_bitfield = bitfield @@ -434,6 +450,8 @@ class ThemeItemDef: def __init__( self, name: str, type_name: TypeName, data_name: str, text: Optional[str], default_value: Optional[str] ) -> None: + self.definition_name = "theme item" + self.name = name self.type_name = type_name self.data_name = data_name @@ -443,6 +461,8 @@ class ThemeItemDef: class ClassDef: def __init__(self, name: str) -> None: + self.definition_name = "class" + self.name = name self.constants: OrderedDict[str, ConstantDef] = OrderedDict() self.enums: OrderedDict[str, EnumDef] = OrderedDict() @@ -685,12 +705,12 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: # Brief description if class_def.brief_description is not None: - f.write(rstize_text(class_def.brief_description.strip(), state) + "\n\n") + f.write(rstize_text(class_def.brief_description.strip(), class_def, state) + "\n\n") # Class description if class_def.description is not None and class_def.description.strip() != "": f.write(make_heading("Description", "-")) - f.write(rstize_text(class_def.description.strip(), state) + "\n\n") + f.write(rstize_text(class_def.description.strip(), class_def, state) + "\n\n") # Online tutorials if len(class_def.tutorials) > 0: @@ -764,7 +784,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("- {}\n\n".format(signature)) if signal.description is not None and signal.description.strip() != "": - f.write(rstize_text(signal.description.strip(), state) + "\n\n") + f.write(rstize_text(signal.description.strip(), signal, state) + "\n\n") index += 1 @@ -794,7 +814,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("- **{}** = **{}**".format(value.name, value.value)) if value.text is not None and value.text.strip() != "": # If value.text contains a bullet point list, each entry needs additional indentation - f.write(" --- " + indent_bullets(rstize_text(value.text.strip(), state))) + f.write(" --- " + indent_bullets(rstize_text(value.text.strip(), value, state))) f.write("\n\n") @@ -811,7 +831,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: for constant in class_def.constants.values(): f.write("- **{}** = **{}**".format(constant.name, constant.value)) if constant.text is not None and constant.text.strip() != "": - f.write(" --- " + rstize_text(constant.text.strip(), state)) + f.write(" --- " + rstize_text(constant.text.strip(), constant, state)) f.write("\n\n") @@ -832,7 +852,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("- {}\n\n".format(signature)) if m.description is not None and m.description.strip() != "": - f.write(rstize_text(m.description.strip(), state) + "\n\n") + f.write(rstize_text(m.description.strip(), m, state) + "\n\n") index += 1 @@ -864,7 +884,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: format_table(f, info) if property_def.text is not None and property_def.text.strip() != "": - f.write(rstize_text(property_def.text.strip(), state) + "\n\n") + f.write(rstize_text(property_def.text.strip(), property_def, state) + "\n\n") index += 1 @@ -885,7 +905,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("- {} {}\n\n".format(ret_type, signature)) if m.description is not None and m.description.strip() != "": - f.write(rstize_text(m.description.strip(), state) + "\n\n") + f.write(rstize_text(m.description.strip(), m, state) + "\n\n") index += 1 @@ -905,7 +925,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("- {} {}\n\n".format(ret_type, signature)) if m.description is not None and m.description.strip() != "": - f.write(rstize_text(m.description.strip(), state) + "\n\n") + f.write(rstize_text(m.description.strip(), m, state) + "\n\n") index += 1 @@ -929,7 +949,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: f.write("- {} {}\n\n".format(ret_type, signature)) if m.description is not None and m.description.strip() != "": - f.write(rstize_text(m.description.strip(), state) + "\n\n") + f.write(rstize_text(m.description.strip(), m, state) + "\n\n") index += 1 @@ -954,7 +974,7 @@ def make_rst_class(class_def: ClassDef, state: State, dry_run: bool, output_dir: format_table(f, info) if theme_item_def.text is not None and theme_item_def.text.strip() != "": - f.write(rstize_text(theme_item_def.text.strip(), state) + "\n\n") + f.write(rstize_text(theme_item_def.text.strip(), theme_item_def, state) + "\n\n") index += 1 @@ -1032,7 +1052,11 @@ def format_codeblock(code_type: str, post_text: str, indent_level: int, state: S return ("\n[" + code_type + "]" + code_text + post_text, len("\n[" + code_type + "]" + code_text)) -def rstize_text(text: str, state: State) -> str: +def rstize_text( + text: str, + context: Union[ClassDef, SignalDef, ConstantDef, AnnotationDef, PropertyDef, MethodDef, ThemeItemDef, None], + state: State, +) -> str: # Linebreak + tabs in the XML should become two line breaks unless in a "codeblock" pos = 0 while True: @@ -1099,6 +1123,7 @@ def rstize_text(text: str, state: State) -> str: else: # command cmd = tag_text space_pos = tag_text.find(" ") + if cmd == "/codeblock" or cmd == "/gdscript" or cmd == "/csharp": tag_text = "" tag_depth -= 1 @@ -1144,11 +1169,13 @@ def rstize_text(text: str, state: State) -> str: '{}.xml: Unresolved constructor "{}".'.format(state.current_class, param), state ) ref_type = "_constructor" - if cmd.startswith("method"): + + elif cmd.startswith("method"): if method_param not in class_def.methods: print_error('{}.xml: Unresolved method "{}".'.format(state.current_class, param), state) ref_type = "_method" - if cmd.startswith("operator"): + + elif cmd.startswith("operator"): if method_param not in class_def.operators: print_error('{}.xml: Unresolved operator "{}".'.format(state.current_class, param), state) ref_type = "_operator" @@ -1213,6 +1240,56 @@ def rstize_text(text: str, state: State) -> str: tag_text = ":ref:`{}<class_{}{}_{}>`".format(repl_text, class_param, ref_type, method_param) escape_pre = True escape_post = True + elif cmd.startswith("param"): + param_name: str = "" + if space_pos >= 0: + param_name = tag_text[space_pos + 1 :].strip() + + if param_name == "": + context_name: str = "unknown context" + if context is not None: + context_name = '{} "{}" description'.format(context.definition_name, context.name) + + print_error( + "{}.xml: Empty argument reference in {}.".format(state.current_class, context_name), + state, + ) + else: + valid_context = ( + isinstance(context, MethodDef) + or isinstance(context, SignalDef) + or isinstance(context, AnnotationDef) + ) + if not valid_context: + context_name: str = "unknown context" + if context is not None: + context_name = '{} "{}" description'.format(context.definition_name, context.name) + + print_error( + '{}.xml: Argument reference "{}" used outside of method, signal, or annotation context in {}.'.format( + state.current_class, param_name, context_name + ), + state, + ) + else: + context_params: List[ParameterDef] = context.parameters + found = False + for param_def in context_params: + if param_def.name == param_name: + found = True + break + if not found: + print_error( + '{}.xml: Unresolved argument reference "{}" in {} "{}" description.'.format( + state.current_class, param_name, context.definition_name, context.name + ), + state, + ) + + if param_name == "": + tag_text = "" + else: + tag_text = "``{}``".format(param_name) elif cmd.find("image=") == 0: tag_text = "" # '![](' + cmd[6:] + ')' elif cmd.find("url=") == 0: diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 5c3038c468..8d58469684 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1803,6 +1803,21 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) { p_rt->pop(); pos = brk_end + 1; + } else if (tag.begins_with("param ")) { + const int tag_end = tag.find(" "); + const String param_name = tag.substr(tag_end + 1, tag.length()).lstrip(" "); + + // Use monospace font with translucent background color to make code easier to distinguish from other text. + p_rt->push_font(doc_code_font); + p_rt->push_bgcolor(Color(0.5, 0.5, 0.5, 0.15)); + p_rt->push_color(code_color); + p_rt->add_text(param_name); + p_rt->pop(); + p_rt->pop(); + p_rt->pop(); + + pos = brk_end + 1; + } else if (doc->class_list.has(tag)) { // Class reference tag such as [Node2D] or [SceneTree]. // Use monospace font with translucent colored background color to make clickable references |