-
Notifications
You must be signed in to change notification settings - Fork 0
/
doc2md.py
122 lines (96 loc) · 3.73 KB
/
doc2md.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# -*- coding: utf-8 -*-
# Copyright (c) 2023 Javier Escalada Gómez
# All rights reserved.
# License: BSD 3-Clause Clear License (see LICENSE for details)
"""
Format the documentation from JSON to markdown.
"""
__author__ = "Javier Escalada Gómez"
__email__ = "kerrigan29a@gmail.com"
__version__ = "0.5.0"
__license__ = "BSD 3-Clause Clear License"
from contextlib import contextmanager
def format(doc, level, url=None):
"""Format the documentation."""
def format_node(node, level, prefix):
""" Format a node. """
nonlocal url
if not node["text"]:
return
header = f"{'#' * level} {node['type']}"
id = f"{prefix}{escape_markdown(node['name'])}"
if url:
path, line = node["location"]
yield f"{header} [{id}]({url.format(path=path, line=line)})\n"
else:
yield f"{header} {id}\n"
yield node["text"]
yield "\n\n"
for node in node["content"]:
yield from format_node(node, level + 1, f"{id}.")
def format_refs(refs):
""" Format the references.
It generates two destination link for every element in the JSON file.
One with the text as the destination, and another with the text surrounded by backticks.
"""
yield f"<!-- references -->\n"
for text, (destination, title) in refs.items():
yield f'[{text}]: #{destination} "{title}"\n'
yield f'[`{text}`]: #{destination} "{title}"\n'
for node in doc["content"]:
yield from format_node(node, level, "")
yield "\n"
yield from format_refs(collect_refs(doc))
def escape_markdown(text):
""" Escape the markdown characters. """
return text.replace("_", "\\_").replace("*", "\\*").replace("`", "\\`")
def collect_refs(node):
"""
Collect the references from the JSON file.
To compose the link, it uses the standard GitHub approach:
1. Start with the header text.
2. Convert all letters to lowercase.
3. Replace all spaces and non-alphanumeric characters with hyphens.
"""
def collect(node, refs, prefix):
id = f"{prefix}{node['name']}"
refs[id] = (f"{node['type']}-{id}".replace(".", "-").lower(), f"{node['type']} {node['name']}")
for node in node["content"]:
collect(node, refs, f"{id}.")
refs = {}
for node in node["content"]:
collect(node, refs, "")
return refs
@contextmanager
def reader(input):
""" Open the input file, or stdin if not specified. """
if input is None:
yield sys.stdin
else:
with input.open("r") as f:
yield f
@contextmanager
def writer(output):
""" Open the output file, or stdout if not specified. """
if output is None:
yield sys.stdout
else:
with output.open("w") as f:
yield f
if __name__ == "__main__":
import sys
import argparse
import json
from pathlib import Path
parser = argparse.ArgumentParser(description="Generate markdown documentation from JSON")
parser.add_argument("-i", "--input", type=Path, default=None,
help="Input file. If not specified, the input is read from stdin")
parser.add_argument("-o", "--output", type=Path, default=None,
help="Output file. If not specified, the output is written to stdout")
parser.add_argument("-l", "--level", type=int, default=1,
help="Start level for the headers")
parser.add_argument("-u", "--url", default=None,
help="URL template for the links")
args = parser.parse_args()
with reader(args.input) as r, writer(args.output) as w:
w.writelines(format(json.load(r), args.level, args.url))