| 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() |