| import argparse | |
| from typing import Any, Dict, TypedDict, Union, Optional, TextIO | |
| from dataclasses import dataclass | |
| import json | |
| import re | |
| PropertyDef = Dict[str, Any] | |
| class Property: | |
| name: str | |
| path: str | |
| type: str | |
| description: str | |
| default: Optional[str] | |
| def __init__(self, definition: PropertyDef): | |
| self.name = definition["Name"] | |
| self.path = definition["Path"] | |
| self.type = definition["Type"] | |
| self.description = definition.get("Description", "").strip() | |
| self.default = None | |
| has_default_unsigned = definition.get("HasDefaultUnsignedValue") | |
| has_default_bool = definition.get("HasDefaultBooleanValue") | |
| has_default_str = definition.get("HasDefaultStringValue") | |
| if has_default_bool == 1: | |
| assert has_default_unsigned | |
| self.default = ( | |
| "true" if definition.get("DefaultUnsignedValue", 0) != 0 else "false" | |
| ) | |
| elif has_default_unsigned: | |
| self.default = str(definition.get("DefaultUnsignedValue", 0)) | |
| elif has_default_str: | |
| self.default = definition.get("DefaultStringValue") | |
| class PropertyGroup(TypedDict): | |
| path: str | |
| """The full path to this group separated by dots (e.g. 'target.process')""" | |
| properties: list[Property] | |
| @dataclass | |
| class PropertyTree: | |
| items: dict[str, Union["PropertyTree", Property]] | |
| def append_property(tree: PropertyTree, prop: Property): | |
| segments = prop.path.split(".") if prop.path else [] | |
| subtree = tree | |
| for segment in segments: | |
| if segment not in subtree.items: | |
| subtree.items[segment] = PropertyTree(items={}) | |
| subtree = subtree.items[segment] | |
| assert isinstance(subtree, PropertyTree) | |
| subtree.items[prop.name] = prop | |
| def wrap_inline_code(text: str): | |
| n_backticks = max([len(s) for s in re.findall("`+", text)], default=0) | |
| fence = "`" * (n_backticks + 1) | |
| if text.startswith("`") or text.endswith("`"): | |
| text = f" {text} " | |
| return f"{fence}{text}{fence}" | |
| def print_property(f: TextIO, path: str, property: Property): | |
| # Invoke lldbsetting directive (lldb/docs/_ext/lldb_setting.py) | |
| f.write(f"```{{lldbsetting}} {path}\n") | |
| f.write(f':type: "{property.type}"\n\n') | |
| f.write(property.description) | |
| f.write("\n\n") | |
| if property.default: | |
| f.write(f":default: {wrap_inline_code(property.default)}\n") | |
| # FIXME: add enumerations (":enum {name}: {description}") | |
| f.write("```\n") | |
| def print_tree(f: TextIO, level: int, prefix: str, name: str, tree: PropertyTree): | |
| if level > 0: | |
| f.write(f"{'#' * (level + 1)} {name}\n\n") | |
| leafs = sorted( | |
| filter(lambda it: isinstance(it[1], Property), tree.items.items()), | |
| key=lambda it: it[0], | |
| ) | |
| for key, prop in leafs: | |
| assert isinstance(prop, Property) # only needed for typing | |
| path = f"{prefix}.{key}" if prefix else key | |
| print_property(f, path, prop) | |
| groups = sorted( | |
| filter(lambda it: isinstance(it[1], PropertyTree), tree.items.items()), | |
| key=lambda it: it[0], | |
| ) | |
| for key, subtree in groups: | |
| assert isinstance(subtree, PropertyTree) # only needed for typing | |
| sub_prefix = f"{prefix}.{key}" if prefix else key | |
| print_tree(f, level + 1, sub_prefix, key, subtree) | |
| HEADER = """ | |
| # Settings | |
| This page lists all possible settings in LLDB. | |
| Settings can be set using `settings set <name> <value>`. | |
| Values can be added to arrays and dictionaries with `settings append -- <name> <value>`. | |
| ```{note} | |
| Some settings only exist for particular LLDB build configurations and so will | |
| not be present in all copies of LLDB. | |
| ``` | |
| """ | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| prog="gen-property-docs-from-json", | |
| description="Generate Markdown from multiple property docs", | |
| ) | |
| parser.add_argument("-o", "--output", help="Path to output file") | |
| parser.add_argument("inputs", nargs="*") | |
| args = parser.parse_args() | |
| root = PropertyTree(items={}) | |
| for input in args.inputs: | |
| with open(input, encoding="utf-8") as f: | |
| properties: dict[str, PropertyDef] = json.load(f) | |
| for key, prop in properties.items(): | |
| if key.startswith("!"): | |
| continue # tablegen metadata | |
| if "Property" not in prop["!superclasses"]: | |
| continue # not a property | |
| append_property(root, Property(prop)) | |
| with open(args.output, "w", encoding="utf-8") as f: | |
| f.write(HEADER) | |
| print_tree(f, 0, "", "", root) | |
| if __name__ == "__main__": | |
| main() |