diff options
author | Jorge Araya Navarro <elcorreo@deshackra.com> | 2015-09-27 19:25:13 -0600 |
---|---|---|
committer | Jorge Araya Navarro <elcorreo@deshackra.com> | 2015-09-27 19:25:13 -0600 |
commit | 7a008afc67cbbdc26e38477621856c04193280b6 (patch) | |
tree | 5613f96194ee35743bf18089f748241626f63537 | |
parent | f95f099eb2337c9e367bb46f61324b709c42973e (diff) |
XML to Open Project Wiki implemented
-rw-r--r-- | tools/docdump/makedocs.py | 264 |
1 files changed, 229 insertions, 35 deletions
diff --git a/tools/docdump/makedocs.py b/tools/docdump/makedocs.py index d11515e94a..81469eed9c 100644 --- a/tools/docdump/makedocs.py +++ b/tools/docdump/makedocs.py @@ -9,16 +9,47 @@ # Contributor: Jorge Araya Navarro <elcorreo@deshackra.com> # +# IMPORTANT NOTICE: +# If you are going to modify anything from this file, please be sure to follow +# the Style Guide for Python Code or often called "PEP8". To do this +# automagically just install autopep8: +# +# $ sudo pip3 install autopep8 +# +# and run: +# +# $ autopep8 makedocs.py +# +# Before committing your changes. Also be sure to delete any trailing +# whitespace you may left. +# +# TODO: +# * Refactor code. Refactor strings. +# * Adapt this script for generating content in other markup formats like +# DokuWiki, Markdown, etc. +# * Because people will translate class_list.xml, we should implement +# internalization. +# +# Also check other TODO entries in this script for more information on what is +# left to do. + import re import argparse import logging from os import path -from itertools import izip_longest +from itertools import zip_longest from xml.etree import ElementTree +# add an option to change the verbosity logging.basicConfig(level=logging.INFO) +def tb(string): + """ Return a byte representation of a string + """ + return bytes(string, "UTF-8") + + def getxmlfloc(): """ Returns the supposed location of the XML file """ @@ -30,7 +61,7 @@ def sortkey(c): """ Symbols are first, letters second """ if "_" == c.attrib["name"][0]: - return True + return "A" else: return c.attrib["name"] @@ -50,7 +81,8 @@ def toOP(text): text = text.replace(group.group(0), "\n\n", 1) elif gd["command"] == "div": if gd["value"] == '"center"': - alignstr = "{display:block; margin-left:auto; margin-right:auto;}" + alignstr = ("{display:block; margin-left:auto;" + " margin-right:auto;}") elif gd["value"] == '"left"': alignstr = "<" elif gd["value"] == '"right"': @@ -68,9 +100,115 @@ def toOP(text): text = text.replace(group.group(0), "_", 1) elif gd["command"] == "u" or gd["command"] == "/u": text = text.replace(group.group(0), "+", 1) - # TODO: Process other non-html commands + # Process other non-html commands + groups = re.finditer((r'\[method ((?P<class>[aA0-zZ9_]+)(?:\.))' + r'?(?P<method>[aA0-zZ9_]+)\]'), text) + for group in groups: + gd = group.groupdict() + if gd["class"]: + replacewith = ("\"<code>{gclass}.{method}</code>(Go " + "to page {gclass}, section {method})\"" + ":/class_{lkclass}#{lkmethod}". + format(gclass=gd["class"], + method=gd["method"], + lkclass=gd["class"].lower(), + lkmethod=gd["method"].lower())) + else: + # The method is located in the same wiki page + replacewith = ("\"<code>{method}</code>(Jump to method" + " {method})\":#{lkmethod}". + format(method=gd["method"], + lkmethod=gd["method"].lower())) + + text = text.replace(group.group(0), replacewith, 1) + # Finally, [Classes] are around brackets, make them direct links + groups = re.finditer(r'\[(?P<class>[az0-AZ0_]+)\]', text) + for group in groups: + gd = group.groupdict() + replacewith = ("\"<code>{gclass}</code>(Go to page of class" + " {gclass})\":/class_{lkclass}". + format(gclass=gd["class"], + lkclass=gd["class"].lower())) + text = text.replace(group.group(0), replacewith, 1) + return text + "\n\n" + +def mkfn(node, is_signal=False): + """ Return a string containing a unsorted item for a function + """ + finalstr = "" + name = node.attrib["name"] + rtype = node.find("return") + if rtype: + rtype = rtype.attrib["type"] + else: + rtype = "void" + # write the return type and the function name first + finalstr += "* " + # return type + if not is_signal: + if rtype != "void": + finalstr += " \"{rtype}({title})\":/class_{link} ".format( + rtype=rtype, + title="Go to page of class " + rtype, + link=rtype.lower()) + else: + finalstr += " void " + + # function name + if not is_signal: + finalstr += "\"*{funcname}*({title})\":#{link} <b>(</b> ".format( + funcname=name, + title="Jump to description for node " + name, + link=name.lower()) + else: + # Signals have no description + finalstr += "*{funcname}* <b>(</b>".format(funcname=name) + # loop for the arguments of the function, if any + args = [] + for arg in sorted( + node.iter(tag="argument"), + key=lambda a: int(a.attrib["index"])): + + twd = "{type} {name}={default}" + tnd = "{type} {name}" + tlk = ("\"*{cls}*(Go to page of class {cls})" + "\":/class_{lcls}") + ntype = arg.attrib["type"] + nname = arg.attrib["name"] + + if "default" in arg.attrib: + args.insert( + -1, + twd.format( + type=tlk.format( + cls=ntype, + lcls=ntype.lower()), + name=nname, + default=arg.attrib["default"])) + else: + # No default value present + args.insert( + -1, + tnd.format( + type=tlk.format( + cls=ntype, + lcls=ntype.lower()), + name=nname)) + # join the arguments together + finalstr += ", ".join(args) + # and, close the function with a ) + finalstr += " <b>)</b>" + # write the qualifier, if any + if "qualifiers" in node.attrib: + qualifier = node.attrib["qualifiers"] + finalstr += " " + qualifier + + finalstr += "\n" + + return finalstr + desc = "Generates documentation from a XML file to different markup languages" parser = argparse.ArgumentParser(description=desc) @@ -100,18 +238,20 @@ if "version" not in root.attrib: version = root.attrib["version"] classes = sorted(root, key=sortkey) +logging.debug("Number of classes: {}".format(len(classes))) +logging.debug("len(classes) / 2 + 1: {}".format(int(len(classes) / 2 + 1))) # first column is always longer, second column of classes should be shorter -zclasses = izip_longest(classes[:len(classes) / 2 + 1], - classes[len(classes) / 2 + 1:], - fillvalue="") +zclasses = zip_longest(classes[:int(len(classes) / 2 + 1)], + classes[int(len(classes) / 2 + 1):], + fillvalue="") # We write the class_list file and also each class file at once with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # Write header of table - fcl.write("|^.\n") - fcl.write("|_. Index symbol |_. Class name " - "|_. Index symbol |_. Class name |\n") - fcl.write("|-.\n") + fcl.write(tb("|^.\n")) + fcl.write(tb("|_. Index symbol |_. Class name " + "|_. Index symbol |_. Class name |\n")) + fcl.write(tb("|-.\n")) indexletterl = "" indexletterr = "" @@ -120,65 +260,119 @@ with open(path.join(args.outputdir, "class_list.txt"), "wb") as fcl: # write the index symbol column, left if indexletterl != gdclassl.attrib["name"][0]: indexletterl = gdclassl.attrib["name"][0] - fcl.write("| *{}* |".format(indexletterl.upper())) + fcl.write(tb("| *{}* |".format(indexletterl.upper()))) else: # empty cell - fcl.write("| |") + fcl.write(tb("| |")) # write the class name column, left - fcl.write("\"{name}({title})\":/class_{link}".format( + fcl.write(tb("\"{name}({title})\":/class_{link}".format( name=gdclassl.attrib["name"], title="Go to page of class " + gdclassl.attrib["name"], - link="class_" + gdclassl.attrib["name"].lower())) + link="class_" + gdclassl.attrib["name"].lower()))) # write the index symbol column, right if isinstance(gdclassr, ElementTree.Element): if indexletterr != gdclassr.attrib["name"][0]: indexletterr = gdclassr.attrib["name"][0] - fcl.write("| *{}* |".format(indexletterr.upper())) + fcl.write(tb("| *{}* |".format(indexletterr.upper()))) else: # empty cell - fcl.write("| |") + fcl.write(tb("| |")) # We are dealing with an empty string else: # two empty cell - fcl.write("| | |\n") + fcl.write(tb("| | |\n")) # We won't get the name of the class since there is no ElementTree # object for the right side of the tuple, so we iterate the next # tuple instead continue # write the class name column (if any), right - fcl.write("\"{name}({title})\":/{link} |\n".format( + fcl.write(tb("\"{name}({title})\":/{link} |\n".format( name=gdclassr.attrib["name"], title="Go to page of class " + gdclassr.attrib["name"], - link=gdclassr.attrib["name"].lower())) + link=gdclassr.attrib["name"].lower()))) # row written # # now, let's write each class page for each class for gdclass in [gdclassl, gdclassr]: - if not isinstance(ElementTree.Element, gdclass): + if not isinstance(gdclass, ElementTree.Element): continue classname = gdclass.attrib["name"] with open(path.join(args.outputdir, "{}.txt".format( classname.lower())), "wb") as clsf: # First level header with the name of the class - clsf.write("h1. {}\n".format(classname)) + clsf.write(tb("h1. {}\n\n".format(classname))) # lay the attributes if "inherits" in gdclass.attrib: inh = gdclass.attrib["inherits"].strip() - clsf.write( - "*Inherits:* \"{name}({title})\":/class_{link}\n". - format( - name=classname, - title="Go to page of class " + classname, - link=classname.lower())) + clsf.write(tb(("h4. Inherits: \"<code>{name}" + "</code>({title})\":/class_{link}\n\n"). + format( + name=inh, + title="Go to page of class " + inh, + link=classname.lower()))) if "category" in gdclass.attrib: - clsf.write("*Category:* {}". - format(gdclass.attrib["category"].strip())) + clsf.write(tb("h4. Category: {}\n\n". + format(gdclass.attrib["category"].strip()))) # lay child nodes - for gdchild in gdclass.iter(): - if gdchild.tag == "brief_description": - clsf.write("h2. Brief Description\n") - clsf.write(toOP(gdchild.text.strip())) - # convert commands in text + briefd = gdclass.find("brief_description") + logging.debug( + "Brief description was found?: {}".format(briefd)) + if briefd.text.strip(): + logging.debug( + "Text: {}".format(briefd.text.strip())) + clsf.write(tb("h2. Brief Description\n\n")) + clsf.write(tb(toOP(briefd.text.strip()) + + "\"read more\":#more\n\n")) + + # Write the list of member functions of this class + methods = gdclass.find("methods") + if methods and len(methods) > 0: + clsf.write(tb("\nh3. Member Functions\n\n")) + for method in methods.iter(tag='method'): + clsf.write(tb(mkfn(method))) + + signals = gdclass.find("signals") + if signals and len(signals) > 0: + clsf.write(tb("\nh3. Signals\n\n")) + for signal in signals.iter(tag='signal'): + clsf.write(tb(mkfn(signal, True))) + # TODO: <members> tag is necessary to process? it does not + # exists in class_list.xml file. + + consts = gdclass.find("constants") + if consts and len(consts) > 0: + clsf.write(tb("\nh3. Numeric Constants\n\n")) + for const in sorted(consts, key=lambda k: + k.attrib["name"]): + if const.text.strip(): + clsf.write(tb("* *{name}* = *{value}* - {desc}\n". + format( + name=const.attrib["name"], + value=const.attrib["value"], + desc=const.text.strip()))) + else: + # Constant have no description + clsf.write(tb("* *{name}* = *{value}*\n". + format( + name=const.attrib["name"], + value=const.attrib["value"]))) + descrip = gdclass.find("description") + clsf.write(tb("\nh3(#more). Description\n\n")) + if descrip.text: + clsf.write(tb(descrip.text.strip() + "\n")) + else: + clsf.write(tb("_Nothing here, yet..._\n")) + + # and finally, the description for each method + if methods and len(methods) > 0: + clsf.write(tb("\nh3. Member Function Description\n\n")) + for method in methods.iter(tag='method'): + clsf.write(tb("h4(#{n}). {name}\n\n".format( + n=method.attrib["name"].lower(), + name=method.attrib["name"]))) + clsf.write(tb(mkfn(method) + "\n")) + clsf.write(tb(toOP(method.find( + "description").text.strip()))) |