mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
Add script to automatically generate helpers documentation (#538)
* Add code to generate helper doc compatible with Simone's bootstrap * Disable markdown parsing + misc cosmetic stuff * Render categories in alphabetic order
This commit is contained in:
parent
95827438c5
commit
89540c1b0b
2 changed files with 264 additions and 0 deletions
171
doc/generate_helper_doc.py
Normal file
171
doc/generate_helper_doc.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
#!/usr/env/python2.7
|
||||
|
||||
import os
|
||||
import glob
|
||||
import datetime
|
||||
|
||||
def render(data):
|
||||
|
||||
from jinja2 import Template
|
||||
from ansi2html import Ansi2HTMLConverter
|
||||
from ansi2html.style import get_styles
|
||||
|
||||
conv = Ansi2HTMLConverter()
|
||||
shell_css = "\n".join(map(str, get_styles(conv.dark_bg, conv.scheme)))
|
||||
|
||||
def shell_to_html(shell):
|
||||
return conv.convert(shell, False)
|
||||
|
||||
template = open("helper_doc_template.html", "r").read()
|
||||
t = Template(template)
|
||||
t.globals['now'] = datetime.datetime.utcnow
|
||||
result = t.render(data=data, convert=shell_to_html, shell_css=shell_css)
|
||||
open("helpers.html", "w").write(result)
|
||||
|
||||
##############################################################################
|
||||
|
||||
class Parser():
|
||||
|
||||
def __init__(self, filename):
|
||||
|
||||
self.file = open(filename, "r").readlines()
|
||||
self.blocks = None
|
||||
|
||||
def parse_blocks(self):
|
||||
|
||||
self.blocks = []
|
||||
|
||||
current_reading = "void"
|
||||
current_block = { "name": None,
|
||||
"line": -1,
|
||||
"comments": [],
|
||||
"code": [] }
|
||||
|
||||
for i, line in enumerate(self.file):
|
||||
|
||||
line = line.rstrip().replace("\t", " ")
|
||||
|
||||
if current_reading == "void":
|
||||
if is_global_comment(line):
|
||||
# We start a new comment bloc
|
||||
current_reading = "comments"
|
||||
assert line.startswith("# ") or line == "#", malformed_error(i)
|
||||
current_block["comments"].append(line[2:])
|
||||
else:
|
||||
pass
|
||||
#assert line == "", malformed_error(i)
|
||||
continue
|
||||
|
||||
elif current_reading == "comments":
|
||||
if is_global_comment(line):
|
||||
# We're still in a comment bloc
|
||||
assert line.startswith("# ") or line == "#", malformed_error(i)
|
||||
current_block["comments"].append(line[2:])
|
||||
else:
|
||||
# We're getting out of a comment bloc, we should find
|
||||
# the name of the function
|
||||
assert len(line.split()) >= 1
|
||||
current_block["line"] = i
|
||||
current_block["name"] = line.split()[0].strip("(){")
|
||||
# Then we expect to read the function
|
||||
current_reading = "code"
|
||||
|
||||
continue
|
||||
|
||||
elif current_reading == "code":
|
||||
|
||||
if line == "}":
|
||||
# We're getting out of the function
|
||||
current_reading = "void"
|
||||
|
||||
# Then we keep this bloc and start a new one
|
||||
# (we ignore helpers containing [internal] ...)
|
||||
if not "[internal]" in current_block["comments"]:
|
||||
self.blocks.append(current_block)
|
||||
current_block = { "name": None,
|
||||
"line": -1,
|
||||
"comments": [],
|
||||
"code": [] }
|
||||
else:
|
||||
current_block["code"].append(line)
|
||||
pass
|
||||
|
||||
continue
|
||||
|
||||
def parse_block(self, b):
|
||||
|
||||
b["brief"] = ""
|
||||
b["details"] = ""
|
||||
b["usage"] = ""
|
||||
b["args"] = []
|
||||
b["ret"] = ""
|
||||
b["example"] = ""
|
||||
|
||||
subblocks = '\n'.join(b["comments"]).split("\n\n")
|
||||
|
||||
for i, subblock in enumerate(subblocks):
|
||||
subblock = subblock.strip()
|
||||
|
||||
if i == 0:
|
||||
b["brief"] = subblock
|
||||
continue
|
||||
|
||||
elif subblock.startswith("example"):
|
||||
b["example"] = " ".join(subblock.split()[1:])
|
||||
continue
|
||||
|
||||
elif subblock.startswith("usage"):
|
||||
for line in subblock.split("\n"):
|
||||
|
||||
if line.startswith("| arg"):
|
||||
argname = line.split()[2]
|
||||
argdescr = " ".join(line.split()[4:])
|
||||
b["args"].append((argname, argdescr))
|
||||
elif line.startswith("| ret"):
|
||||
b["ret"] = " ".join(line.split()[2:])
|
||||
else:
|
||||
if line.startswith("usage"):
|
||||
line = " ".join(line.split()[1:])
|
||||
b["usage"] += line + "\n"
|
||||
continue
|
||||
|
||||
elif subblock.startswith("| arg"):
|
||||
for line in subblock.split("\n"):
|
||||
if line.startswith("| arg"):
|
||||
argname = line.split()[2]
|
||||
argdescr = line.split()[4:]
|
||||
b["args"].append((argname, argdescr))
|
||||
continue
|
||||
|
||||
else:
|
||||
b["details"] += subblock + "\n\n"
|
||||
|
||||
b["usage"] = b["usage"].strip()
|
||||
|
||||
|
||||
def is_global_comment(line):
|
||||
return line.startswith('#')
|
||||
|
||||
def malformed_error(line_number):
|
||||
import pdb; pdb.set_trace()
|
||||
return "Malformed file line {} ?".format(line_number)
|
||||
|
||||
def main():
|
||||
|
||||
helper_files = sorted(glob.glob("../data/helpers.d/*"))
|
||||
helpers = []
|
||||
|
||||
for helper_file in helper_files:
|
||||
category_name = os.path.basename(helper_file)
|
||||
print "Parsing %s ..." % category_name
|
||||
p = Parser(helper_file)
|
||||
p.parse_blocks()
|
||||
for b in p.blocks:
|
||||
p.parse_block(b)
|
||||
|
||||
helpers.append((category_name, p.blocks))
|
||||
|
||||
render(helpers)
|
||||
|
||||
main()
|
||||
|
93
doc/helper_doc_template.html
Normal file
93
doc/helper_doc_template.html
Normal file
|
@ -0,0 +1,93 @@
|
|||
<!-- NO_MARKDOWN_PARSING -->
|
||||
|
||||
<h1>App helpers</h1>
|
||||
|
||||
{% for category, helpers in data %}
|
||||
|
||||
<h3 style="text-transform: uppercase; font-weight: bold">{{ category }}</h3>
|
||||
|
||||
{% for h in helpers %}
|
||||
|
||||
<div class="helper-card">
|
||||
<div class="helper-card-body">
|
||||
<div data-toggle="collapse" href="#collapse-{{ h.name }}" style="cursor:pointer">
|
||||
<h5 class="helper-card-title"><tt>{{ h.name }}</tt></h5>
|
||||
<h6 class="helper-card-subtitle text-muted">{{ h.brief }}</h6>
|
||||
</div>
|
||||
<div id="collapse-{{ h.name }}" class="collapse" role="tabpanel">
|
||||
<hr style="margin-top:25px; margin-bottom:25px;">
|
||||
<p>
|
||||
{% if not '\n' in h.usage %}
|
||||
<strong>Usage</strong>: <code class="helper-code">{{ h.usage }}</code>
|
||||
{% else %}
|
||||
<strong>Usage</strong>: <code class="helper-code helper-usage">{{ h.usage }}</code>
|
||||
{% endif %}
|
||||
</p>
|
||||
{% if h.args %}
|
||||
<p>
|
||||
<strong>Arguments</strong>:
|
||||
<ul>
|
||||
{% for name, descr in h.args %}
|
||||
<li><code>{{ name }}</code> : {{ descr }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if h.ret %}
|
||||
<p>
|
||||
<strong>Returns</strong>: {{ h.ret }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if h.example %}
|
||||
<p>
|
||||
<strong>Example</strong>: <code class="helper-code">{{ h.example }}</code>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if h.details %}
|
||||
<p>
|
||||
<strong>Details</strong>:
|
||||
<p>
|
||||
{{ h.details.replace('\n', '</br>') }}
|
||||
</p>
|
||||
</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
<a href="https://github.com/YunoHost/yunohost/blob/stretch-unstable/data/helpers.d/{{ category }}#L{{ h.line + 1 }}">Dude, show me the code !</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<style>
|
||||
/*=================================================
|
||||
Helper card
|
||||
=================================================*/
|
||||
.helper-card {
|
||||
width:100%;
|
||||
min-height: 1px;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
border: 1px solid rgba(0,0,0,.125);
|
||||
border-radius: 0.5rem;
|
||||
word-wrap: break-word;
|
||||
margin-top: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
.helper-card-body {
|
||||
padding: 1.25rem;
|
||||
padding-top: 0.8rem;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.helper-code {
|
||||
word-wrap: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
/*===============================================*/
|
||||
|
||||
</style>
|
Loading…
Add table
Reference in a new issue