-
Notifications
You must be signed in to change notification settings - Fork 224
/
Copy pathmarkdown_parser.py
127 lines (92 loc) · 3.69 KB
/
markdown_parser.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
123
124
125
126
127
import re
from pathlib import Path
import dash_bootstrap_components as dbc
import markdown
from dash import dcc, html
from .helpers import (
ExampleContainer,
HighlightedSource,
load_source_with_environment,
)
__all__ = ["parse"]
HERE = Path(__file__).parent
HEADER_PATTERN = re.compile(r"---.*---", flags=re.DOTALL)
SPLIT_PATTERN = re.compile(r"{{.*}}")
EXAMPLE_DOC_PATTERN = re.compile(r"{{(.*)}}")
# To format the docstrings
VERBATIM_PATTERN = re.compile(r"`((\\\[)((.|\n)*?)(\\]))`")
LINK_PATTERN = re.compile(r"\\\[([\w\.\-:\/]+)\\\]\(([\w\.\-:#\/]+)\)")
PROP_OPTIONAL_DEFAULT_PATTERN = re.compile(r"""default (.*)\)""")
PROP_TYPE_PATTERN = re.compile(r"""(\s*- \w+?\s*\()([^;]*);""")
PROP_NAME_PATTERN = re.compile(r"""(\n- )(\w+?) \(""")
NESTED_PROP_NAME_PATTERN = re.compile(r"""(\n\s+- )(\w+?) \(""")
def parse(app, markdown_path, extra_env_vars=None):
extra_env_vars = extra_env_vars or {}
raw = (HERE / markdown_path).read_text()
# we use the markdown package to extract metadata
md = markdown.Markdown(extensions=["meta"])
md.convert(raw)
meta = md.Meta
content = [
html.H1(meta["title"][0]),
html.Div(dcc.Markdown(meta["lead"][0]), className="lead"),
]
raw = HEADER_PATTERN.sub("", raw).strip()
markdown_blocks = SPLIT_PATTERN.split(raw)
markdown_blocks = [dcc.Markdown(block.strip()) for block in markdown_blocks]
examples_docs = EXAMPLE_DOC_PATTERN.findall(raw)
examples_docs = [
_parse_block(block, app, extra_env_vars) for block in examples_docs
]
content.extend(_interleave(markdown_blocks, examples_docs))
return html.Div(content, key=str(markdown_path))
def _parse_block(block, app, extra_env_vars):
type_, data = block.split(":", 1)
if type_ == "example":
return _parse_example(data, app, extra_env_vars)
elif type_ == "apidoc":
_, filename = data.rsplit("/", 1)
component_name = filename[:-3]
return component_reference(component_name)
elif type_ == "code-example":
return _parse_code_example(data)
raise ValueError("Unable to parse block.")
def _parse_example(data, app, extra_env_vars):
source_path, obj = data.split(":")
source = (HERE / source_path).read_text().strip()
example = load_source_with_environment(source, obj, {"app": app, **extra_env_vars})
return ExampleContainer(example, source)
def _parse_code_example(filename):
source_path = HERE / filename
source = (HERE / source_path).read_text().strip()
return html.Div(
HighlightedSource(source),
className="border source-container rounded mb-3",
)
def _interleave(l1, l2):
n = len(l2)
out = []
for x in zip(l1[:n], l2):
out.extend(x)
out.extend(l1[n:])
return out
def component_reference(component_name):
component = getattr(dbc, component_name)
component_doc = component.__doc__
return_div = [dcc.Markdown("### Keyword arguments for {}".format(component_name))]
docs = component_doc.split("Keyword arguments:")[-1]
docs = re.sub(VERBATIM_PATTERN, r"`[\3]`", docs)
# format links
docs = re.sub(LINK_PATTERN, r"[\1](\2)", docs)
# formats the prop defaults
docs = re.sub(PROP_OPTIONAL_DEFAULT_PATTERN, r"default `\1`)", docs)
# formats the prop type
docs = re.sub(PROP_TYPE_PATTERN, r"\1*\2*;", docs)
# formats the prop name on first level only
docs = re.sub(PROP_NAME_PATTERN, r"\1**`\2`** (", docs)
# formats keys of nested dicts
docs = re.sub(NESTED_PROP_NAME_PATTERN, r"\1**`\2`** (", docs)
# removes a level of nesting
docs = docs.replace("\n-", "\n")
return_div.append(dcc.Markdown(docs))
return html.Div(return_div, className="reference")