wayland/doc/book/protocol-to-markdown.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

189 lines
5.4 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
import sys
from xml.dom import Node
from xml.dom.minidom import parse
if len(sys.argv) != 2:
print("usage: protocol-to-markdown.py <xml>")
sys.exit(1)
filename = sys.argv[1]
doc = parse(filename)
assert doc.documentElement.tagName == "protocol"
def parse_protocol(node):
protocol = {
"copyright": None,
"summary": None,
"description": None,
"interfaces": [],
}
for child in filter(is_element, node.childNodes):
match child.tagName:
case "copyright":
protocol["copyright"] = parse_text(child)
case "description":
protocol |= parse_description(child)
case "interface":
protocol["interfaces"].append(parse_interface(child))
return protocol
def parse_interface(node):
interface = {
"name": node.getAttribute("name"),
"version": node.getAttribute("version"),
"frozen": node.getAttribute("frozen") == "true",
"summary": None,
"description": None,
"requests": [],
"events": [],
"enums": [],
}
for child in filter(is_element, node.childNodes):
match child.tagName:
case "description":
interface |= parse_description(child)
case "request":
interface["requests"].append(parse_message(child))
case "event":
interface["events"].append(parse_message(child))
case "enum":
interface["enums"].append(parse_enum(child))
return interface
def parse_message(node):
message = {
"name": node.getAttribute("name"),
"type": node.getAttribute("type"),
"since": node.getAttribute("since"),
"deprecated-since": node.getAttribute("deprecated-since"),
"summary": None,
"description": None,
"args": [],
}
for child in filter(is_element, node.childNodes):
match child.tagName:
case "description":
message |= parse_description(child)
case "arg":
message["args"].append(parse_arg(child))
return message
def parse_arg(node):
arg = {
"name": node.getAttribute("name"),
"type": node.getAttribute("type"),
"summary": node.getAttribute("summary"),
"description": None,
"interface": node.getAttribute("interface"),
"allow-null": node.getAttribute("allow-null") == "true",
"enum": node.getAttribute("enum"),
}
for child in filter(is_element, node.childNodes):
if child.tagName == "description":
arg |= parse_description(child)
return arg
def parse_enum(node):
enum = {
"name": node.getAttribute("name"),
"since": node.getAttribute("since"),
"bitfield": node.getAttribute("bitfield") == "true",
"summary": None,
"description": None,
"entries": [],
}
for child in filter(is_element, node.childNodes):
match child.tagName:
case "description":
enum |= parse_description(child)
case "entry":
enum["entries"].append(parse_entry(child))
return enum
def parse_entry(node):
entry = {
"name": node.getAttribute("name"),
"value": node.getAttribute("value"),
"summary": node.getAttribute("summary"),
"since": node.getAttribute("since"),
"deprecated-since": node.getAttribute("deprecated-since"),
}
for child in filter(is_element, node.childNodes):
if child.tagName == "description":
entry |= parse_description(child)
return entry
def parse_description(node):
return {
"summary": node.getAttribute("summary"),
"description": parse_text(node),
}
def parse_text(node):
raw = "".join([child.data for child in node.childNodes])
raw = raw.lstrip("\n")
start = len(raw) - len(raw.lstrip())
indent = raw[:start]
lines = [l.removeprefix(indent) for l in raw.split("\n")]
return "\n".join(lines).strip()
def is_element(node):
return node.nodeType == Node.ELEMENT_NODE
def render_protocol(protocol):
if protocol["copyright"]:
print("```")
print(protocol["copyright"])
print("```")
if protocol["description"]:
print(protocol["description"])
for interface in protocol["interfaces"]:
render_interface(interface)
def render_interface(interface):
render_header("##", interface)
for request in interface["requests"]:
render_message(request)
for event in interface["events"]:
render_message(event)
for enum in interface["enums"]:
render_enum(enum)
def render_message(message):
render_header("###", message)
for arg in message["args"]:
print(f"`{arg["name"]}`")
print(f" : `{arg["type"]}` — {arg["summary"]}")
print("")
def render_enum(enum):
render_header("###", enum)
for entry in enum["entries"]:
print(f"`{entry["name"]}`")
print(f" : `{entry["value"]}` — {entry["summary"]}")
print("")
def render_header(heading, element):
print("")
print(f"{heading} `{element["name"]}`", end="")
if element["summary"]:
print(f"{element["summary"]}")
else:
print("")
print("")
if element["description"]:
print(element["description"])
print("")
protocol = parse_protocol(doc.documentElement)
render_protocol(protocol)