blob: 5d90f9f7ec8e1c62dcc74d54e74eba58c4b54528 [file]
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()