From 02525c784360c3e1a268be0901cfc03b0022454c Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Wed, 4 Dec 2013 21:11:44 +0100 Subject: [PATCH] [DEV] better documentation generated --- cppParser/CppHeaderParser/CppHeaderParser.py | 6 +- lutinDoc.py | 109 +++++++- lutinDocHtml.py | 265 ++++++++++++------- lutinModule.py | 10 +- theme/base.css | 33 ++- 5 files changed, 312 insertions(+), 111 deletions(-) diff --git a/cppParser/CppHeaderParser/CppHeaderParser.py b/cppParser/CppHeaderParser/CppHeaderParser.py index c8d1087..3e0c4da 100644 --- a/cppParser/CppHeaderParser/CppHeaderParser.py +++ b/cppParser/CppHeaderParser/CppHeaderParser.py @@ -357,19 +357,19 @@ class CppClass(dict): 'methods': { 'public':[], - 'protected':[], + 'protected':[], 'private':[] }, 'properties': { 'public':[], - 'protected':[], + 'protected':[], 'private':[] }, 'enums': { 'public':[], - 'protected':[], + 'protected':[], 'private':[] } } diff --git a/lutinDoc.py b/lutinDoc.py index d2971c3..c5ee045 100644 --- a/lutinDoc.py +++ b/lutinDoc.py @@ -5,14 +5,109 @@ import lutinTools # TODO : Add try of generic input ... sys.path.append(lutinTools.GetCurrentPath(__file__) + "/ply/ply/") sys.path.append(lutinTools.GetCurrentPath(__file__) + "/cppParser/CppHeaderParser/") +import CppHeaderParser import lutinDocHtml import lutinDocMd +## +## @brief Main Documantion class +## @param[in] moduleName Name of the module of this element +## +class doc: + def __init__(self, moduleName): + self.moduleName = moduleName + self.listClass = dict() + self.listVariable = dict() + self.listFunction = dict() + self.listNamepsaces = dict() + + + ## + ## @brief Add a File at the parsing system + ## @param[in] filename File To add at the parsing element system. + ## @return True if no error occured, False otherwise + ## + def add_file(self, filename): + debug.debug("adding file in documantation : '" + filename + "'"); + try: + metaData = CppHeaderParser.CppHeader(filename) + except CppHeaderParser.CppParseError, e: + debug.error(" can not parse the file: '" + filename + "' error : " + e) + return False + + # add all classes : + for element in metaData.classes: + localClass = metaData.classes[element] + if localClass['namespace'] == '': + className = localClass['name'] + else: + className = localClass['namespace'] + "::" + localClass['name'] + if className in self.listClass.keys(): + debug.warning("Might merge class : '" + className + "' file : " + filename) + else: + self.listClass[className] = localClass + + # add all namespaces: + + # add all global vars: + + # add all global function: + + return True + + ## + ## @brief Generate Documentation at the folder ... + ## @param[in] destFolder Destination folder. + ## @param[in] mode (optinnal) generation output mode {html, markdown ...} + ## + def generate_documantation(self, destFolder, mode="html"): + if mode == "html": + if lutinDocHtml.generate(self, destFolder) == False: + debug.warning("Generation Documentation :'" + mode + "' ==> return an error for " + self.moduleName) + elif mode == "markdown": + # todo ... + None + else: + debug.error("Unknow Documantation mode generation :'" + mode + "'") + return False + return True + + ## + ## @brief Get the heritage list (parent) of one element. + ## @param[in] element Element name. + ## @return List of all element herited + ## + def get_heritage_list(self, element): + list = [] + # get element class : + if element in self.listClass.keys(): + localClass = self.listClass[element] + if len(localClass['inherits']) != 0: + # TODO : Support multiple heritage ... + isFirst = True + for heritedClass in localClass['inherits']: + if isFirst == True: + list = self.get_heritage_list(heritedClass['class']) + break; + debug.verbose("find parent : " + element) + list.append(element); + return list + + ## + ## @brief Get the heritage list (child) of this element. + ## @param[in] curentClassName Element name. + ## @return List of all childs + ## + def get_down_heritage_list(self, curentClassName): + list = [] + # get element class : + for element in self.listClass: + localClass = self.listClass[element] + if len(localClass['inherits']) != 0: + for heritedClass in localClass['inherits']: + if curentClassName == heritedClass['class']: + list.append(element) + break; + debug.verbose("find childs : " + str(list)) + return list - - -def GenerateDocFile(filename, outFolder, isHtml=True) : - if isHtml == True: - lutinDocHtml.GenerateDocFile(filename, outFolder) - else: - lutinDocDot.GenerateDocFile(filename, outFolder) \ No newline at end of file diff --git a/lutinDocHtml.py b/lutinDocHtml.py index 9e78e73..2aa6c7d 100644 --- a/lutinDocHtml.py +++ b/lutinDocHtml.py @@ -3,6 +3,62 @@ import lutinDebug as debug import sys import lutinTools import CppHeaderParser +import re + +global_class_link = { + "std::string" : "http://www.cplusplus.com/reference/string/string/", + "std::u16string" : "http://www.cplusplus.com/reference/string/u16string/", + "std::u32string" : "http://www.cplusplus.com/reference/string/u32string/", + "std::wstring" : "http://www.cplusplus.com/reference/string/wstring/", + "std::vector" : "http://www.cplusplus.com/reference/vector/vector/" + } + + +def replace_type(match): + value = "" + match.group() + "" + return value + +def replace_storage_keyword(match): + value = "" + match.group() + "" + return value + +def display_color(valBase): + # storage keyword : + p = re.compile("(inline|const|class|virtual|private|public|protected|friend|const|extern|auto|register|static|unsigned|signed|volatile|char|double|float|int|long|short|void|typedef|struct|union|enum)") + val = p.sub(replace_storage_keyword, valBase) + # type : + p = re.compile("(bool|BOOL|char(16_t|32_t)?|double|float|u?int(8|16|32|64|128)?(_t)?|long|short|signed|size_t|unsigned|void|(I|U)(8|16|32|64|128))") + val = p.sub(replace_type, val) + return val, len(valBase) + +def display_type(type, localClassName): + type = type.replace("inline ", "") + lenght = 0; + isFirst = True + out = '' + # we split all the element in list sepa=rated with space to keep class... and standard c+ class + for element in type.split(' '): + if isFirst == False: + out += " " + lenght += 1 + isFirst = False + # check if the element in internal at the current lib + if element[:len(localClassName)] == localClassName: + out += "" + element + "" + lenght += len(element) + # check if the element is in local Lib: + elif False: + None + # Ckeck if the variable in a standard class: + elif element in global_class_link.keys(): + out += "" + element + "" + lenght += len(element) + else: + data, lenghtTmp = display_color(element) + out += data + lenght += lenghtTmp + # get every subelement class : + return [out,lenght] def display_doxygen_param(comment, input, output): data = "Parameter" @@ -15,6 +71,7 @@ def display_doxygen_param(comment, input, output): val = comment.find(" ") var = comment[:val] endComment = comment[val:] + # TODO : Check if it exist in the parameter list ... data += "" + var + " " + endComment data += "
" @@ -38,9 +95,9 @@ def parse_doxygen(data) : data = data.replace("\n **", "\n") data = data.replace("\n * ", "\n") data = data.replace("\n *", "\n") - data = data.replace("\r", "") + data = data.replace("\r", '') streams = data.split("@") - data2 = "" + data2 = '' for element in streams: if element[:1] == "\n" \ or element[:2] == "\n\n": @@ -60,7 +117,7 @@ def parse_doxygen(data) : data2 += element[5:] data2 += "
" - data3 = "" + data3 = '' for element in streams: if element[:1] == "\n" \ or element[:2] == "\n\n": @@ -79,41 +136,44 @@ def parse_doxygen(data) : data3 += "Return: " data3 += element[7:] data3 += "
" - if data3 != "": + if data3 != '': data2 += "\n" return data2 - - -def writeExpendSize(data, size) : - ret = data +def white_space(size) : + ret = '' for iii in range(len(ret), size): ret += " " return ret -def displayReductFunction(function, file, classement, sizeReturn, sizefunction) : - lineData = classement + " " - +def displayReductFunction(function, file, classement, sizeReturn, sizefunction, localClassName) : + file.write(classement + " ") + lenght = len(classement)+1; if function['destructor'] : - lineData += writeExpendSize("", sizeReturn) - lineData += "~" + file.write(white_space(sizeReturn) + "~") + lenght += sizeReturn+1; elif function['constructor'] : - lineData += writeExpendSize("", sizeReturn+1) + file.write(white_space(sizeReturn+1)) + lenght += sizeReturn+1; else : - lineData += writeExpendSize(function["rtnType"], sizeReturn+1) - parameterPos = len(lineData) + sizefunction+2; - lineData +="" + function["name"] + "" - lineData += writeExpendSize("", sizefunction+1 - len(function["name"])) - lineData += "(" - file.write(lineData); + typeData, typeLen = display_type(function["rtnType"], localClassName); + file.write(typeData) + file.write(white_space(sizeReturn+1 - typeLen)) + lenght += sizeReturn+1; + parameterPos = lenght + sizefunction+2; + file.write("" + function["name"] + "") + file.write(white_space(sizefunction+1 - len(function["name"]))) + file.write("(") isFirst = True for param in function["parameters"]: if isFirst == False: file.write(",
") - file.write(writeExpendSize("",parameterPos)) - file.write(param['type']) + file.write(white_space(parameterPos)) + + typeData, typeLen = display_type(param["type"], localClassName); + file.write(typeData) if param['name'] != "": file.write(" ") file.write("" + param['name'] + "") @@ -122,7 +182,7 @@ def displayReductFunction(function, file, classement, sizeReturn, sizefunction) file.write("
") -def displayFunction(namespace, function, file, classement, sizeReturn, sizefunction) : +def displayFunction(namespace, function, file, classement, sizeReturn, sizefunction, localClassName) : lineData = "" if ( function['constructor'] == True \ or function['destructor'] == True \ @@ -136,22 +196,24 @@ def displayFunction(namespace, function, file, classement, sizeReturn, sizefunct file.write("
\n");
 	if function['destructor'] :
-		lineData = "~"
+		file.write("~")
+		lenght = 1;
 	elif function['constructor'] :
-		lineData = ""
+		lenght = 0;
 	else :
-		lineData = function["rtnType"] + " "
+		typeData, typeLen = display_type(function["rtnType"], localClassName);
+		file.write(typeData + " ")
+		lenght = typeLen+1;
 	
-	parameterPos = len(lineData) + len(function["name"]) + 1;
-	lineData += "" + function["name"] + ""
-	lineData += "("
-	file.write(lineData);
+	parameterPos = lenght + len(function["name"]) + 1;
+	file.write("" + function["name"] + "(")
 	isFirst = True
 	for param in function["parameters"]:
 		if isFirst == False:
 			file.write(",\n")
-			file.write(writeExpendSize("", parameterPos))
-		file.write(param['type'])
+			file.write(white_space(parameterPos))
+		typeData, typeLen = display_type(param["type"], localClassName);
+		file.write(typeData)
 		if param['name'] != "":
 			file.write(" ")
 			file.write("" + param['name'] + "")
@@ -178,53 +240,68 @@ def calsulateSizeReturn(function, size) :
 		return len(function["rtnType"])+1
 	return size
 
-def add_header(file):
-	file.write("\n")
-	file.write("\n")
-	file.write("\n")
-	file.write("	\n")
-	file.write("	Ewol Library\n")
-	file.write("	\n")
-	file.write("\n")
-	file.write("\n")
-	file.write("	
\n"); - file.write("
\n") - file.write(" Ewol Library\n") - file.write("
\n") - file.write("
\n") - file.write("
\n") +def class_name_to_file_name(className): + className = className.replace(":", "_") + className = className.replace(" ", "") + className += ".html" + return className -def add_buttom(file): - file.write("
\n") - file.write("\n") - file.write("\n") - -def GenerateDocFile(filename, outFolder) : - try: - metaData = CppHeaderParser.CppHeader(filename) - except CppHeaderParser.CppParseError, e: - debug.error(" can not parse the file: '" + filename + "' error : " + e) - return False - - lutinTools.CreateDirectoryOfFile(outFolder+"/"); +def generate(myDoc, outFolder) : lutinTools.CopyFile(lutinTools.GetCurrentPath(__file__)+"/theme/base.css", outFolder+"/base.css") - - for element in metaData.classes: - classFileName = outFolder + "/" - localClass = metaData.classes[element] - if localClass['namespace'] == "": - className = localClass['name'] + # create common header + genericHeader = "\n" + genericHeader += "\n" + genericHeader += "\n" + genericHeader += " \n" + genericHeader += " Ewol Library\n" + genericHeader += " \n" + genericHeader += "\n" + genericHeader += "\n" + genericHeader += "
\n" + genericHeader += "
\n" + genericHeader += "

Ewol Library

\n" + #genericHeader += "
    \n" + baseNamespace = "" + for className in sorted(myDoc.listClass.iterkeys()) : + pos = className.find("::") + if pos >= 0: + namespace = className[:pos] + rest = className[pos+2:] else: - className = localClass['namespace'] + "::" + localClass['name'] + namespace = "" + rest = className + if baseNamespace != namespace: + if baseNamespace != "": + genericHeader += "
\n" + genericHeader += "
  • " + namespace + "
  • \n" + genericHeader += "
      \n" + baseNamespace = namespace + + genericHeader += "
    • " + rest + "
    • \n" + + if baseNamespace != "": + genericHeader += "
    \n" + #genericHeader += " \n" + genericHeader += "
    \n" + genericHeader += "
    \n" + genericHeader += "
    \n" + + genericFooter = "
    \n" + genericFooter += "\n" + genericFooter += "\n" + + + for className in sorted(myDoc.listClass.iterkeys()) : + localClass = myDoc.listClass[className] debug.debug(" class: " + className) - classFileName += className - # Replace all :: with __ - classFileName = classFileName.replace(":", "_") - classFileName = classFileName.replace(" ", "") - classFileName += ".html" + classFileName = outFolder + "/" + class_name_to_file_name(className) + # create directory (in case) + lutinTools.CreateDirectoryOfFile(classFileName); + debug.printElement("doc", myDoc.moduleName, "<==", className) + # open the file : file = open(classFileName, "w") - add_header(file) + file.write(genericHeader) file.write("

    " + className + "

    \n") file.write("\n") @@ -247,27 +324,34 @@ def GenerateDocFile(filename, outFolder) : # TODO: ... file.write("
    \n");
     		for function in localClass["methods"]["public"]:
    -			displayReductFunction(function, file, "public:   ", sizeReturn, sizefunction)
    +			displayReductFunction(function, file, "+ ", sizeReturn, sizefunction, className)
     		for function in localClass["methods"]["protected"]:
    -			displayReductFunction(function, file, "protected:", sizeReturn, sizefunction)
    +			displayReductFunction(function, file, "# ", sizeReturn, sizefunction, className)
     		for function in localClass["methods"]["private"]:
    -			displayReductFunction(function, file, "private:  ", sizeReturn, sizefunction)
    +			displayReductFunction(function, file, "- ", sizeReturn, sizefunction, className)
     		file.write("
    \n"); file.write("\n") file.write("\n") - - - if len(localClass['inherits']) != 0: + heritage = myDoc.get_heritage_list(className) + heritageDown = myDoc.get_down_heritage_list(className) + if len(heritage) > 1 \ + or len(heritageDown) > 0: file.write("

    Object Hierarchy:

    \n") file.write("
    \n")
    -			for heritedClass in localClass['inherits']:
    -				file.write("" + heritedClass['class'] + "\n")
    -			file.write("    |\n")
    -			file.write("    +--> " +  localClass['name'] + "\n")
    +			level = 0;
    +			for heritedClass in heritage:
    +				if level != 0:
    +					file.write(white_space(level*4) + "+--> ")
    +				if heritedClass != className:
    +					file.write("" + heritedClass + "\n")
    +				else:
    +					file.write("" + heritedClass + "\n")
    +				level += 1;
    +			for heritedClass in heritageDown:
    +				file.write(white_space(level*4) + "+--> ")
    +				file.write("" + heritedClass + "\n")
     			file.write("
    \n") file.write("
    \n") - - """ file.write("

    Signals:

    \n") # display all signals : @@ -287,21 +371,18 @@ def GenerateDocFile(filename, outFolder) : file.write("

    Detail:

    \n") # display all the class internal functions : for function in localClass["methods"]["public"]: - displayFunction(localClass['namespace'] , function, file, "public: ", sizeReturn, sizefunction) + displayFunction(localClass['namespace'] , function, file, "+ ", sizeReturn, sizefunction, className) file.write("\n
    \n") for function in localClass["methods"]["protected"]: - displayFunction(localClass['namespace'] , function, file, "protected:", sizeReturn, sizefunction) + displayFunction(localClass['namespace'] , function, file, "# ", sizeReturn, sizefunction, className) file.write("\n
    \n") for function in localClass["methods"]["private"]: - displayFunction(localClass['namespace'] , function, file, "private: ", sizeReturn, sizefunction) + displayFunction(localClass['namespace'] , function, file, "- ", sizeReturn, sizefunction, className) file.write("\n
    \n") - add_buttom(file) + file.write(genericFooter) file.close() -""" - -""" diff --git a/lutinModule.py b/lutinModule.py index 80a7411..8b0e850 100644 --- a/lutinModule.py +++ b/lutinModule.py @@ -33,11 +33,12 @@ class module: # type of the module: self.type='LIBRARY' # Name of the module - self.name='' + self.name=moduleName # Dependency list: self.depends=[] # Dependency list: self.docPath=lutinTools.GetCurrentPath(file) + self.documentation = lutinDoc.doc(self.name) # export PATH self.export_path=[] self.local_path=[] @@ -72,7 +73,6 @@ class module: raise 'Input value error' self.originFile = file; self.originFolder = lutinTools.GetCurrentPath(self.originFile) - self.name=moduleName self.localHeritage = heritage.heritage(self) self.packageProp = { "COMPAGNY_TYPE" : set(""), @@ -384,14 +384,16 @@ class module: def CreateDoc(self, target) : if self.docPath == "": return False - lutinTools.RemoveFolderAndSubFolder(target.GetDocFolder(self.name)); for root, dirnames, filenames in os.walk(self.docPath): tmpList = fnmatch.filter(filenames, "*.h") # Import the module : for filename in tmpList: fileCompleteName = os.path.join(root, filename) debug.debug(" Find a file : '" + fileCompleteName + "'") - lutinDoc.GenerateDocFile(fileCompleteName, target.GetDocFolder(self.name)); + self.documentation.add_file(fileCompleteName) + # Real creation of the documentation : + lutinTools.RemoveFolderAndSubFolder(target.GetDocFolder(self.name)); + self.documentation.generate_documantation(target.GetDocFolder(self.name)) # call here to build the module diff --git a/theme/base.css b/theme/base.css index a9d293b..aa94d74 100644 --- a/theme/base.css +++ b/theme/base.css @@ -5,7 +5,7 @@ html { body { font-family: 'Ubuntu',Tahoma,sans-serif; - padding-top: 60px; + padding-top: 40px; padding-bottom: 40px; font-size: 15px; line-height: 150%; @@ -13,7 +13,7 @@ body { color: #333333; background-color: #ffffff; display: block; - margin-left: 50px; + margin-left: 250px; margin-right: 50px; }; @@ -38,24 +38,31 @@ body { } .navbar-fixed-top { + width:210px; display: block; position: fixed; padding-top: 0px; top: 0; - min-height: 40px; + height: 100%; right: 0; left: 0; margin-bottom: 0; background-color: #d44413; border: 1px solid #c64012; - font-size: 20px; + font-size: 15px; font-weight: 200; color: #ffffff; text-shadow: 0 1px 0 #ce4213; padding: 10px 20px 10px; margin-left: -20px; + overflow:scroll; + overflow-x:hidden; } - +/* +.navbar ul { + font-size: 15px; +}; +*/ h1, h2, h3, h4, h5, h6 { display: block; margin: 10px 0; @@ -88,6 +95,8 @@ pre { border-radius: 4px; } + + .code-function { text-decoration:none; color:#09857e; @@ -106,3 +115,17 @@ pre { font-weight:bold; } +.code-number { + text-decoration:none; + color:#007b00; +} + +.code-keyword { + text-decoration:none; + color:#215eb8; + font-weight:bold; +} +.code-storage-keyword { + text-decoration:none; + color:#466cb4; +} \ No newline at end of file