Commit 2a3a2356 authored by François Grisez's avatar François Grisez
Browse files

Add code in wrappers generator for reference handling in docstrings

parent 5e077c08
...@@ -480,6 +480,18 @@ class CParser(object): ...@@ -480,6 +480,18 @@ class CParser(object):
else: else:
self.classesIndex[_class.name] = None self.classesIndex[_class.name] = None
self.methodsIndex = {}
for _class in self.cProject.classes:
for funcname in _class.classMethods:
self.methodsIndex[funcname] = None
for funcname in _class.instanceMethods:
self.methodsIndex[funcname] = None
for _property in _class.properties.values():
if _property.setter is not None:
self.methodsIndex[_property.setter.name] = None
if _property.getter is not None:
self.methodsIndex[_property.getter.name] = None
name = NamespaceName() name = NamespaceName()
name.from_snake_case('linphone') name.from_snake_case('linphone')
...@@ -508,8 +520,23 @@ class CParser(object): ...@@ -508,8 +520,23 @@ class CParser(object):
except Error as e: except Error as e:
print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0])) print('Could not parse \'{0}\' class: {1}'.format(_class.name, e.args[0]))
self._clean_all_indexes()
self._fix_all_types() self._fix_all_types()
self._fix_all_docs()
def _clean_all_indexes(self):
for index in [self.classesIndex, self.interfacesIndex, self.methodsIndex]:
self._clean_index(index)
def _clean_index(self, index):
keysToRemove = []
for key in index.keys():
if index[key] is None:
keysToRemove.append(key)
for key in keysToRemove:
del index[key]
def _class_is_refcountable(self, _class): def _class_is_refcountable(self, _class):
if _class.name in self.forcedRefcountableClasses: if _class.name in self.forcedRefcountableClasses:
...@@ -576,6 +603,14 @@ class CParser(object): ...@@ -576,6 +603,14 @@ class CParser(object):
else: else:
raise Error('bctbx_list_t type without specified contained type') raise Error('bctbx_list_t type without specified contained type')
def _fix_all_docs(self):
for _class in self.classesIndex.values():
if _class.briefDescription is not None:
_class.briefDescription.resolve_all_references(self)
for method in self.methodsIndex.values():
if method.briefDescription is not None:
method.briefDescription.resolve_all_references(self)
def parse_enum(self, cenum): def parse_enum(self, cenum):
if 'associatedTypedef' in dir(cenum): if 'associatedTypedef' in dir(cenum):
nameStr = cenum.associatedTypedef.name nameStr = cenum.associatedTypedef.name
...@@ -750,6 +785,7 @@ class CParser(object): ...@@ -750,6 +785,7 @@ class CParser(object):
absArg = Argument(argName, aType) absArg = Argument(argName, aType)
method.add_arguments(absArg) method.add_arguments(absArg)
self.methodsIndex[cfunction.name] = method
return method return method
def parse_type(self, cType): def parse_type(self, cType):
......
...@@ -246,6 +246,7 @@ class Project: ...@@ -246,6 +246,7 @@ class Project:
self.__events = [] self.__events = []
self.__functions = [] self.__functions = []
self.classes = [] self.classes = []
self.docparser = metadoc.Parser()
def add(self, elem): def add(self, elem):
if isinstance(elem, CClass): if isinstance(elem, CClass):
...@@ -387,7 +388,7 @@ class Project: ...@@ -387,7 +388,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
ev.deprecated = True ev.deprecated = True
ev.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() ev.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
ev.briefDoc = metadoc.Description(node.find('./briefdescription')) ev.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
ev.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) ev.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return ev return ev
...@@ -399,7 +400,7 @@ class Project: ...@@ -399,7 +400,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
e.deprecated = True e.deprecated = True
e.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() e.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
e.briefDoc = metadoc.Description(node.find('./briefdescription')) e.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
e.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) e.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
enumvalues = node.findall("enumvalue[@prot='public']") enumvalues = node.findall("enumvalue[@prot='public']")
for enumvalue in enumvalues: for enumvalue in enumvalues:
...@@ -422,7 +423,7 @@ class Project: ...@@ -422,7 +423,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
sm.deprecated = True sm.deprecated = True
sm.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() sm.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
sm.briefDoc = metadoc.Description(node.find('./briefdescription')) sm.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
sm.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) sm.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return sm return sm
...@@ -432,7 +433,7 @@ class Project: ...@@ -432,7 +433,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
s.deprecated = True s.deprecated = True
s.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() s.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
s.briefDoc = metadoc.Description(node.find('./briefdescription')) s.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
s.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) s.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
structmembers = node.findall("sectiondef/memberdef[@kind='variable'][@prot='public']") structmembers = node.findall("sectiondef/memberdef[@kind='variable'][@prot='public']")
for structmember in structmembers: for structmember in structmembers:
...@@ -501,7 +502,7 @@ class Project: ...@@ -501,7 +502,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
f.deprecated = True f.deprecated = True
f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
f.briefDoc = metadoc.Description(node.find('./briefdescription')) f.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return f return f
else: else:
...@@ -513,7 +514,7 @@ class Project: ...@@ -513,7 +514,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
td.deprecated = True td.deprecated = True
td.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() td.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
td.briefDoc = metadoc.Description(node.find('./briefdescription')) td.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
td.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) td.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
return td return td
return None return None
...@@ -573,7 +574,7 @@ class Project: ...@@ -573,7 +574,7 @@ class Project:
if deprecatedNode is not None: if deprecatedNode is not None:
f.deprecated = True f.deprecated = True
f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip() f.briefDescription = ''.join(node.find('./briefdescription').itertext()).strip()
f.briefDoc = metadoc.Description(node.find('./briefdescription')) f.briefDoc = self.docparser.parse_description(node.find('./briefdescription'))
f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription')) f.detailedDescription = self.__cleanDescription(node.find('./detaileddescription'))
if f.briefDescription == '' and ''.join(f.detailedDescription.itertext()).strip() == '': if f.briefDescription == '' and ''.join(f.detailedDescription.itertext()).strip() == '':
return None return None
......
...@@ -16,45 +16,95 @@ ...@@ -16,45 +16,95 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import abstractapi
class Nil: class Nil:
pass pass
class Reference: class Reference:
def __init__(self, name): def __init__(self, cname):
self.cObjectName = name self.cname = cname
self.relatedObject = None
class ClassReference(Reference):
def resolve(self, api):
try:
self.relatedObject = api.classesIndex[self.cname]
except KeyError:
print('doc reference pointing on an unknown object ({0})'.format(self.cname))
class FunctionReference(Reference):
def resolve(self, api):
try:
self.relatedObject = api.methodsIndex[self.cname]
except KeyError:
print('doc reference pointing on an unknown object ({0})'.format(self.cname))
class Paragraph: class Paragraph:
def __init__(self, node=None): def __init__(self):
self.parts = [] self.parts = []
if node is not None:
self.parse_doxygen_node(node)
def parse_doxygen_node(self, node): def resolve_all_references(self, api):
for part in self.parts:
if isinstance(part, Reference):
part.resolve(api)
class Description:
def __init__(self):
self.paragraphs = []
def resolve_all_references(self, api):
for paragraph in self.paragraphs:
paragraph.resolve_all_references(api)
class Parser:
def parse_description(self, node):
desc = Description()
for paraNode in node.findall('./para'):
desc.paragraphs.append(self._parse_paragraph(paraNode))
return desc
def _parse_paragraph(self, node):
paragraph = Paragraph()
for partNode in node.iter(): for partNode in node.iter():
if partNode is node:
text = partNode.text text = partNode.text
if text is not None: if text is not None:
self.parts.append(text) paragraph.parts.append(text)
if partNode is not node: else:
if partNode.tag == 'ref':
ref = self._parse_reference(partNode)
if ref is not None:
paragraph.parts.append(ref)
else:
text = partNode.text
if text is not None:
paragraph.parts.append(text)
tail = partNode.tail tail = partNode.tail
if tail is not None: if tail is not None:
self.parts.append(tail) paragraph.parts.append(tail)
return paragraph
class Description: def _parse_reference(self, node):
def __init__(self, node=None): if node.text.endswith('()'):
self.paragraphs = [] return FunctionReference(node.text[0:-2])
if node is not None: else:
self.parse_doxygen_node(node) return ClassReference(node.text)
def parse_doxygen_node(self, node):
for paraNode in node.findall('./para'):
self.paragraphs.append(Paragraph(paraNode))
class Translator: class Translator:
def __init__(self):
self.textWidth = 80
def translate(self, description): def translate(self, description):
if description is None: if description is None:
return None return None
...@@ -63,9 +113,10 @@ class Translator: ...@@ -63,9 +113,10 @@ class Translator:
for para in description.paragraphs: for para in description.paragraphs:
if para is not description.paragraphs[0]: if para is not description.paragraphs[0]:
lines.append('') lines.append('')
lines.append(''.join(para.parts)) lines.append(self._translate_paragraph(para))
self._tag_as_brief(lines) self._tag_as_brief(lines)
lines = self._crop_text(lines, self.textWidth)
translatedDoc = {'lines': []} translatedDoc = {'lines': []}
for line in lines: for line in lines:
...@@ -73,12 +124,67 @@ class Translator: ...@@ -73,12 +124,67 @@ class Translator:
return translatedDoc return translatedDoc
def _translate_paragraph(self, para):
strPara = ''
for part in para.parts:
if isinstance(part, str):
strPara += part
elif isinstance(part, Reference):
try:
strPara += self._translate_reference(part)
except ReferenceTranslationError as e:
print('could not translate one reference in docstrings ({0})'.format(e.args[0]))
strPara += Translator._translate_reference(self, part)
else:
raise TypeError('untranslatable paragraph element ({0})'.format(part))
return strPara
def _translate_reference(self, ref):
if isinstance(ref, FunctionReference):
return ref.cname + '()'
else:
return ref.cname
def _crop_text(self, inputLines, width):
outputLines = []
for line in inputLines:
outputLines += self._split_line(line, width)
return outputLines
def _split_line(self, line, width):
lines = []
while len(line) > width:
cutIndex = line.rfind(' ', 0, width)
if cutIndex != -1:
lines.append(line[0:cutIndex])
line = line[cutIndex+1:]
else:
cutIndex = width
lines.append(line[0:cutIndex])
line = line[cutIndex:]
lines.append(line)
return lines
class ReferenceTranslationError(RuntimeError):
pass
class DoxygenCppTranslator(Translator): class DoxygenCppTranslator(Translator):
def _tag_as_brief(self, lines): def _tag_as_brief(self, lines):
if len(lines) > 0: if len(lines) > 0:
lines[0] = '@brief ' + lines[0] lines[0] = '@brief ' + lines[0]
def _translate_reference(self, ref):
if isinstance(ref.relatedObject, (abstractapi.Class, abstractapi.Enum)):
return '#' + ref.relatedObject.name.to_c()
elif isinstance(ref.relatedObject, abstractapi.Method):
return ref.relatedObject.name.to_c() + '()'
else:
raise ReferenceTranslationError(ref.cname)
class SandcastleCSharpTranslator(Translator): class SandcastleCSharpTranslator(Translator):
def _tag_as_brief(self, lines): def _tag_as_brief(self, lines):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment