From 6c0c6c81c3a7a5c5a191d65540419b9ef08abbd5 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Sun, 16 Apr 2017 16:21:24 +0100 Subject: [PATCH 01/32] Markdown output added. --- cldoc/clang/cindex.py | 3 +- cldoc/cmdgenerate.py | 13 +- cldoc/generators/__init__.py | 1 + cldoc/generators/html.py | 3 +- cldoc/generators/md.py | 660 +++++++++++++++++++++++++++++++++++ 5 files changed, 676 insertions(+), 4 deletions(-) create mode 100644 cldoc/generators/md.py diff --git a/cldoc/clang/cindex.py b/cldoc/clang/cindex.py index e5782e8..d98ec90 100644 --- a/cldoc/clang/cindex.py +++ b/cldoc/clang/cindex.py @@ -1516,7 +1516,8 @@ def spelling(self): @staticmethod def from_id(id): if id >= len(TypeKind._kinds) or TypeKind._kinds[id] is None: - raise ValueError,'Unknown type kind %d' % id + return TypeKind.UNEXPOSED; + #raise ValueError,'Unknown type kind %d' % id return TypeKind._kinds[id] def __repr__(self): diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index cb44df9..83d641e 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -16,9 +16,10 @@ from . import fs, staticsite from . import log +import glob def run_generate(t, opts): - if opts.type != 'html' and opts.type != 'xml': + if opts.type != 'html' and opts.type != 'xml' and opts.type != 'md': return from . import generators @@ -32,6 +33,10 @@ def run_generate(t, opts): xmlout = os.path.join(baseout, 'xml') generator.generate(xmlout) + if opts.type == 'md': + generator_md = generators.Md(t, opts) + mdout = os.path.join(baseout, 'md') + generator_md.generate(mdout) if opts.type == 'html': generators.Html(t).generate(baseout, opts.static, opts.custom_js, opts.custom_css) @@ -94,7 +99,11 @@ def run(args): cxxflags = args[:sep] opts = parser.parse_args(restargs) - + newfiles=[] + for filepath in opts.files: + gfiles=glob.glob(filepath) + newfiles=newfiles+gfiles + opts.files=newfiles if opts.quiet: sys.stdout = open(os.devnull, 'w') diff --git a/cldoc/generators/__init__.py b/cldoc/generators/__init__.py index 844cc70..add75da 100644 --- a/cldoc/generators/__init__.py +++ b/cldoc/generators/__init__.py @@ -12,6 +12,7 @@ # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from .xml import Xml from .html import Html +from .md import Md from .search import Search from .report import Report diff --git a/cldoc/generators/html.py b/cldoc/generators/html.py index 1181b9f..79fbe3c 100644 --- a/cldoc/generators/html.py +++ b/cldoc/generators/html.py @@ -50,7 +50,8 @@ def generate(self, output, isstatic, customjs=[], customcss=[]): with fs.fs.open(outfile, 'w') as o: o.write(content) - + + Generator.generate(self, outdir) if "CLDOC_DEV" in os.environ: fs.fs.rmtree(os.path.join(output, "javascript"), True) fs.fs.copytree(os.path.join(datadir, "javascript"), os.path.join(output, "javascript")) diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py new file mode 100644 index 0000000..f674cf3 --- /dev/null +++ b/cldoc/generators/md.py @@ -0,0 +1,660 @@ +# This file is part of cldoc. cldoc is free software: you can +# redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 2. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, write to the Free Software Foundation, Inc., 51 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +from __future__ import absolute_import +from cldoc.clang import cindex + +from .generator import Generator +from cldoc import nodes +from cldoc import example +from cldoc import utf8 + +from xml.etree import ElementTree +import sys, os + +from cldoc import fs + +class Md(Generator): + def __init__(self, tree=None, opts=None): + self.tree = tree + self.options = opts + self.namespaces_as_directories=True + + def generate(self, outdir): + if not outdir: + outdir = 'md' + + try: + fs.fs.makedirs(outdir) + except OSError: + pass + + ElementTree.register_namespace('gobject', 'http://jessevdk.github.com/cldoc/gobject/1.0') + ElementTree.register_namespace('cldoc', 'http://jessevdk.github.com/cldoc/1.0') + + self.index = ElementTree.Element('index') + self.written = {} + + self.indexmap = { + self.tree.root: self.index + } + + cm = self.tree.root.comment + + if cm: + if cm.brief: + self.index.append(self.doc_to_md(self.tree.root, cm.brief, 'brief')) + + if cm.doc: + self.index.append(self.doc_to_md(self.tree.root, cm.doc)) + + Generator.generate(self, outdir) + + if self.options.report: + self.add_report() + + self.write_md(self.index, 'index.md') + + print('Generated `{0}\''.format(outdir)) + + def add_report(self): + from .report import Report + + reportname = 'report' + + while reportname + '.md' in self.written: + reportname = '_' + reportname + + page = Report(self.tree, self.options).generate(reportname) + + elem = ElementTree.Element('report') + elem.set('name', 'Documentation generator') + elem.set('ref', reportname) + + self.index.append(elem) + + self.write_md(page, reportname + '.md') + + def indent(self, elem, level=0): + i = "\n" + " " * level + + if elem.tag == 'doc': + return + + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + + for e in elem: + self.indent(e, level + 1) + + if not e.tail or not e.tail.strip(): + e.tail = i + " " + if not e.tail or not e.tail.strip(): + e.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def ref_to_link(self,rf): + parts=rf.split('#') + if parts.count==2: + link=parts[1] + link=link.replace('::','.')+'.md' + else: + link='' + return link + + def link_md(self,f, title, rf): + lnk=self.ref_to_link(rf) + f.write('['+title+']('+lnk+')') + + def list_bases(self,f,elem): + for child in elem.getchildren(): + self.indent(child) + if child.tag=='type': + title=child.attrib['name'] + ref='' + if child.attrib.has_key('ref'): + ref=child.attrib['ref'] + self.link_md(f,title,ref) + f.write('\n') + + def return_type(self,elem): + ret_type=dict() + for child in elem.getchildren(): + if child.tag=='type': + ret_type['type']=child.attrib['name'] + return ret_type + + def argument(self,elem): + ret_arg=dict() + ret_arg['name']=elem.attrib['name'] + for child in elem.getchildren(): + if child.tag=='type': + ret_arg['type']=child.attrib['name'] + return ret_arg + + def doc_method(self,f,elem): + arguments=[] + ret_type=dict() + for child in elem.getchildren(): + if child.tag=='return': + ret_type=self.return_type(child) + elif child.tag=='argument': + arguments.append(self.argument(child)) + f.write(ret_type['type']+' '+elem.attrib['name']) + args_txt=[] + for arg in arguments: + args_txt.append(arg['name']) + f.write(','.join(args_txt)) + f.write('\n------\n\n') + + def write_md(self, elem, fname): + + self.written[fname] = True + + elem.attrib['xmlns'] = 'http://jessevdk.github.com/cldoc/1.0' + + tree = ElementTree.ElementTree(elem) + + self.indent(tree.getroot()) + title=elem.attrib['name'] + layout_name='reference' + + fullpath=os.path.join(self.outdir, fname) + + try: + head_tail=os.path.split(fullpath) + os.makedirs(head_tail[0]) + except: + pass + + f = fs.fs.open(fullpath, 'w') + f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\n---\n') + + f.write(elem.tag+' ') + f.write(elem.attrib['id']+'\n') + f.write('===\n') + # children: + for child in elem.getchildren(): + self.indent(child) + if child.tag=='Class': + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + self.link_md(f,title,ref) + elif child.tag=='base': + self.list_bases(f,child) + elif child.tag=='constructor': + pass + elif child.tag=='method': + self.doc_method(f,child) + #tree.write(f, encoding='utf-8', xml_declaration=True) + + f.close() + + def is_page(self, node): + if node.force_page: + return True + + if isinstance(node, nodes.Struct) and node.is_anonymous: + return False + + if isinstance(node, nodes.Class): + for child in node.children: + if not (isinstance(child, nodes.Field) or \ + isinstance(child, nodes.Variable) or \ + isinstance(child, nodes.TemplateTypeParameter)): + return True + + return False + + pagecls = [nodes.Namespace, nodes.Category, nodes.Root] + + for cls in pagecls: + if isinstance(node, cls): + return True + + if isinstance(node, nodes.Typedef) and len(node.children) > 0: + return True + + return False + + def is_top(self, node): + if self.is_page(node): + return True + + if node.parent == self.tree.root: + return True + + return False + + def refid(self, node): + if not node._refid is None: + return node._refid + + parent = node + + meid = node.qid + + if not node.parent or (isinstance(node.parent, nodes.Root) and not self.is_page(node)): + return 'index#' + meid + + # Find topmost parent + while not self.is_page(parent): + parent = parent.parent + + if not node is None: + node._refid = parent.qid + '#' + meid + return node._refid + else: + return None + + def add_ref_node_id(self, node, elem): + r = self.refid(node) + + if not r is None: + elem.set('ref', r) + + def add_ref_id(self, cursor, elem): + if not cursor: + return + + if cursor in self.tree.cursor_to_node: + node = self.tree.cursor_to_node[cursor] + elif cursor.get_usr() in self.tree.usr_to_node: + node = self.tree.usr_to_node[cursor.get_usr()] + else: + return + + self.add_ref_node_id(node, elem) + + def type_to_xml(self, tp, parent=None): + elem = ElementTree.Element('type') + + if tp.is_constant_array: + elem.set('size', str(tp.constant_array_size)) + elem.set('class', 'array') + elem.append(self.type_to_xml(tp.element_type, parent)) + elif tp.is_function: + elem.set('class', 'function') + + result = ElementTree.Element('result') + result.append(self.type_to_xml(tp.function_result, parent)) + elem.append(result) + + args = ElementTree.Element('arguments') + elem.append(args) + + for arg in tp.function_arguments: + args.append(self.type_to_xml(arg, parent)) + else: + elem.set('name', tp.typename_for(parent)) + + if len(tp.qualifier) > 0: + elem.set('qualifier', tp.qualifier_string) + + if tp.builtin: + elem.set('builtin', 'yes') + + if tp.is_out: + elem.set('out', 'yes') + + if tp.transfer_ownership != 'none': + elem.set('transfer-ownership', tp.transfer_ownership) + + if tp.allow_none: + elem.set('allow-none', 'yes') + + self.add_ref_id(tp.decl, elem) + return elem + + def enumvalue_to_xml(self, node, elem): + elem.set('value', str(node.value)) + + def enum_to_xml(self, node, elem): + if not node.typedef is None: + elem.set('typedef', 'yes') + + if node.isclass: + elem.set('class', 'yes') + + def struct_to_xml(self, node, elem): + self.class_to_xml(node, elem) + + if not node.typedef is None: + elem.set('typedef', 'yes') + + def templatetypeparameter_to_xml(self, node, elem): + dt = node.default_type + + if not dt is None: + d = ElementTree.Element('default') + + d.append(self.type_to_xml(dt)) + elem.append(d) + + def templatenontypeparameter_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type)) + + def function_to_xml(self, node, elem): + if not (isinstance(node, nodes.Constructor) or + isinstance(node, nodes.Destructor)): + ret = ElementTree.Element('return') + + if not node.comment is None and hasattr(node.comment, 'returns') and node.comment.returns: + ret.append(self.doc_to_md(node, node.comment.returns)) + + tp = self.type_to_xml(node.return_type, node.parent) + + ret.append(tp) + elem.append(ret) + + for arg in node.arguments: + ret = ElementTree.Element('argument') + ret.set('name', arg.name) + ret.set('id', arg.qid) + + if not node.comment is None and arg.name in node.comment.params: + ret.append(self.doc_to_md(node, node.comment.params[arg.name])) + + ret.append(self.type_to_xml(arg.type, node.parent)) + elem.append(ret) + + def method_to_xml(self, node, elem): + self.function_to_xml(node, elem) + + if len(node.override) > 0: + elem.set('override', 'yes') + + for ov in node.override: + ovelem = ElementTree.Element('override') + + ovelem.set('name', ov.qid_to(node.qid)) + self.add_ref_node_id(ov, ovelem) + + elem.append(ovelem) + + if node.virtual: + elem.set('virtual', 'yes') + + if node.static: + elem.set('static', 'yes') + + if node.abstract: + elem.set('abstract', 'yes') + + def typedef_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node)) + + def typedef_to_xml_ref(self, node, elem): + elem.append(self.type_to_xml(node.type, node)) + + def variable_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node.parent)) + + def property_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node.parent)) + + def set_access_attribute(self, node, elem): + if node.access == cindex.CXXAccessSpecifier.PROTECTED: + elem.set('access', 'protected') + elif node.access == cindex.CXXAccessSpecifier.PRIVATE: + elem.set('access', 'private') + elif node.access == cindex.CXXAccessSpecifier.PUBLIC: + elem.set('access', 'public') + + def process_bases(self, node, elem, bases, tagname): + for base in bases: + child = ElementTree.Element(tagname) + + self.set_access_attribute(base, child) + + child.append(self.type_to_xml(base.type, node)) + + if base.node and not base.node.comment is None and base.node.comment.brief: + child.append(self.doc_to_md(base.node, base.node.comment.brief, 'brief')) + + elem.append(child) + + def process_subclasses(self, node, elem, subclasses, tagname): + for subcls in subclasses: + child = ElementTree.Element(tagname) + + self.set_access_attribute(subcls, child) + self.add_ref_node_id(subcls, child) + + child.set('name', subcls.qid_to(node.qid)) + + if not subcls.comment is None and subcls.comment.brief: + child.append(self.doc_to_md(subcls, subcls.comment.brief, 'brief')) + + elem.append(child) + + def class_to_xml(self, node, elem): + self.process_bases(node, elem, node.bases, 'base') + self.process_bases(node, elem, node.implements, 'implements') + + self.process_subclasses(node, elem, node.subclasses, 'subclass') + self.process_subclasses(node, elem, node.implemented_by, 'implementedby') + + hasabstract = False + allabstract = True + + for method in node.methods: + if method.abstract: + hasabstract = True + else: + allabstract = False + + if hasabstract: + if allabstract: + elem.set('interface', 'true') + else: + elem.set('abstract', 'true') + + def field_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node.parent)) + + def doc_to_md(self, parent, doc, tagname='doc'): + doce = ElementTree.Element(tagname) + + s = '' + last = None + + for component in doc.components: + if isinstance(component, utf8.string): + s += component + elif isinstance(component, example.Example): + # Make highlighting + if last is None: + doce.text = s + else: + last.tail = s + + s = '' + + code = ElementTree.Element('code') + doce.append(code) + + last = code + + for item in component: + if item.classes is None: + s += item.text + else: + last.tail = s + + s = '' + par = code + + for cls in item.classes: + e = ElementTree.Element(cls) + + par.append(e) + par = e + + par.text = item.text + last = par + + if last == code: + last.text = s + else: + last.tail = s + + s = '' + last = code + else: + if last is None: + doce.text = s + else: + last.tail = s + + s = '' + + nds = component[0] + refname = component[1] + + # Make multiple refs + for ci in range(len(nds)): + cc = nds[ci] + + last = ElementTree.Element('ref') + + if refname: + last.text = refname + else: + last.text = parent.qlbl_from(cc) + + self.add_ref_node_id(cc, last) + + if ci != len(nds) - 1: + if ci == len(nds) - 2: + last.tail = ' and ' + else: + last.tail = ', ' + + doce.append(last) + + if last is None: + doce.text = s + else: + last.tail = s + + return doce + + def call_type_specific(self, node, elem, fn): + clss = [node.__class__] + + while len(clss) > 0: + cls = clss[0] + clss = clss[1:] + + if cls == nodes.Node: + continue + + nm = cls.__name__.lower() + '_' + fn + + if hasattr(self, nm): + getattr(self, nm)(node, elem) + break + + if cls != nodes.Node: + clss.extend(cls.__bases__) + + def node_to_md(self, node): + elem = ElementTree.Element(node.classname) + props = node.props + + for prop in props: + if props[prop]: + elem.set(prop, props[prop]) + + if not node.comment is None and node.comment.brief: + elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) + + if not node.comment is None and node.comment.doc: + elem.append(self.doc_to_md(node, node.comment.doc)) + + self.call_type_specific(node, elem, 'to_xml') + + for child in node.sorted_children(): + if child.access == cindex.CXXAccessSpecifier.PRIVATE: + continue + + self.refid(child) + + if self.is_page(child): + chelem = self.node_to_md_ref(child) + else: + chelem = self.node_to_md(child) + + elem.append(chelem) + + return elem + + def templated_to_xml_ref(self, node, element): + for child in node.sorted_children(): + if not (isinstance(child, nodes.TemplateTypeParameter) or isinstance(child, nodes.TemplateNonTypeParameter)): + continue + + element.append(self.node_to_md(child)) + + def generate_page(self, node): + elem = self.node_to_md(node) + namespace_separator='.' + if self.namespaces_as_directories==True: + namespace_separator='/' + self.write_md(elem, node.qid.replace('::', namespace_separator) + '.md') + + def node_to_md_ref(self, node): + elem = ElementTree.Element(node.classname) + props = node.props + + # Add reference item to index + self.add_ref_node_id(node, elem) + + if 'name' in props: + elem.set('name', props['name']) + + if not node.comment is None and node.comment.brief: + elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) + + self.call_type_specific(node, elem, 'to_xml_ref') + + return elem + + def generate_node(self, node): + # Ignore private stuff + if node.access == cindex.CXXAccessSpecifier.PRIVATE: + return + + self.refid(node) + + if self.is_page(node): + elem = self.node_to_md_ref(node) + + self.indexmap[node.parent].append(elem) + self.indexmap[node] = elem + + self.generate_page(node) + elif self.is_top(node): + self.index.append(self.node_to_md(node)) + + if isinstance(node, nodes.Namespace) or isinstance(node, nodes.Category): + # Go deep for namespaces and categories + Generator.generate_node(self, node) + elif isinstance(node, nodes.Class): + # Go deep, but only for inner classes + Generator.generate_node(self, node, lambda x: isinstance(x, nodes.Class)) + +# vi:ts=4:et From 44f2843465255f7d6ce3c0781fa588679bbcdab9 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Sun, 16 Apr 2017 18:20:07 +0100 Subject: [PATCH 02/32] Relative links. Xml write disabled. --- cldoc/cmdgenerate.py | 6 ++--- cldoc/generators/md.py | 50 ++++++++++++++++++++++++++++------------- cldoc/generators/xml.py | 2 +- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index 83d641e..bb7d8c1 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -32,11 +32,11 @@ def run_generate(t, opts): baseout = opts.output xmlout = os.path.join(baseout, 'xml') - generator.generate(xmlout) + #generator.generate(xmlout) if opts.type == 'md': generator_md = generators.Md(t, opts) - mdout = os.path.join(baseout, 'md') - generator_md.generate(mdout) + #mdout = os.path.join(baseout, 'md') + generator_md.generate(baseout) if opts.type == 'html': generators.Html(t).generate(baseout, opts.static, opts.custom_js, opts.custom_css) diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index f674cf3..a03703d 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -31,7 +31,7 @@ def __init__(self, tree=None, opts=None): def generate(self, outdir): if not outdir: - outdir = 'md' + outdir = '' try: fs.fs.makedirs(outdir) @@ -107,16 +107,20 @@ def indent(self, elem, level=0): def ref_to_link(self,rf): parts=rf.split('#') - if parts.count==2: + if len(parts)==2: link=parts[1] - link=link.replace('::','.')+'.md' + link=link.replace('::',self.namespace_separator) + fullpath=os.path.join(self.outdir, link) + # Now this link might contain a path, or the current page might have a path. + relpath=os.path.relpath(fullpath,self.current_path).replace('\\','/') + link=relpath else: link='' return link - def link_md(self,f, title, rf): + def link_md(self,title, rf): lnk=self.ref_to_link(rf) - f.write('['+title+']('+lnk+')') + return '['+title+']('+lnk+')' def list_bases(self,f,elem): for child in elem.getchildren(): @@ -126,7 +130,7 @@ def list_bases(self,f,elem): ref='' if child.attrib.has_key('ref'): ref=child.attrib['ref'] - self.link_md(f,title,ref) + f.write(self.link_md(title,ref)) f.write('\n') def return_type(self,elem): @@ -168,36 +172,50 @@ def write_md(self, elem, fname): tree = ElementTree.ElementTree(elem) self.indent(tree.getroot()) - title=elem.attrib['name'] + if elem.attrib.has_key('name'): + title=elem.attrib['name'] + elif elem.tag=='index': + title='Index' + else: + title='Untitled' layout_name='reference' fullpath=os.path.join(self.outdir, fname) + self.current_path='' try: head_tail=os.path.split(fullpath) - os.makedirs(head_tail[0]) + self.current_path=head_tail[0] + os.makedirs(self.current_path) except: pass f = fs.fs.open(fullpath, 'w') f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\n---\n') - f.write(elem.tag+' ') - f.write(elem.attrib['id']+'\n') - f.write('===\n') + if elem.tag=='index': + f.write('Reference') + else: + f.write(elem.tag+' ') + f.write(elem.attrib['id']) + f.write('\n===\n') # children: for child in elem.getchildren(): self.indent(child) - if child.tag=='Class': + if child.tag=='class': title=child.tag+' '+child.attrib['name'] ref=child.attrib['ref'] - self.link_md(f,title,ref) + f.write(self.link_md(title,ref)+'\n') elif child.tag=='base': self.list_bases(f,child) elif child.tag=='constructor': pass elif child.tag=='method': self.doc_method(f,child) + elif child.tag=='namespace': + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + f.write(self.link_md(title,ref)+'\n') #tree.write(f, encoding='utf-8', xml_declaration=True) f.close() @@ -611,10 +629,10 @@ def templated_to_xml_ref(self, node, element): def generate_page(self, node): elem = self.node_to_md(node) - namespace_separator='.' + self.namespace_separator='.' if self.namespaces_as_directories==True: - namespace_separator='/' - self.write_md(elem, node.qid.replace('::', namespace_separator) + '.md') + self.namespace_separator='/' + self.write_md(elem, node.qid.replace('::', self.namespace_separator) + '.md') def node_to_md_ref(self, node): elem = ElementTree.Element(node.classname) diff --git a/cldoc/generators/xml.py b/cldoc/generators/xml.py index faba2b8..5239b89 100644 --- a/cldoc/generators/xml.py +++ b/cldoc/generators/xml.py @@ -108,7 +108,7 @@ def write_xml(self, elem, fname): tree = ElementTree.ElementTree(elem) self.indent(tree.getroot()) - + f = fs.fs.open(os.path.join(self.outdir, fname), 'w') tree.write(f, encoding='utf-8', xml_declaration=True) f.write('\n') From 8d805b340e43216ac6f32088632801e187f2ffda Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Sun, 16 Apr 2017 18:31:45 +0100 Subject: [PATCH 03/32] . --- cldoc/cmdgenerate.py | 2 +- cldoc/generators/md.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index bb7d8c1..e5acf8d 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -32,7 +32,7 @@ def run_generate(t, opts): baseout = opts.output xmlout = os.path.join(baseout, 'xml') - #generator.generate(xmlout) + generator.generate('C:\\Simul\\master\\Simul\\Help\\docout\\xml') if opts.type == 'md': generator_md = generators.Md(t, opts) #mdout = os.path.join(baseout, 'md') diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index a03703d..f034654 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -151,17 +151,28 @@ def argument(self,elem): def doc_method(self,f,elem): arguments=[] ret_type=dict() + doc='' + brief='' for child in elem.getchildren(): if child.tag=='return': ret_type=self.return_type(child) elif child.tag=='argument': arguments.append(self.argument(child)) + elif child.tag=='brief': + brief=child.text + elif child.tag=='doc': + doc=child.text f.write(ret_type['type']+' '+elem.attrib['name']) args_txt=[] for arg in arguments: args_txt.append(arg['name']) - f.write(','.join(args_txt)) + f.write('('+','.join(args_txt)+')') f.write('\n------\n\n') + if brief!='': + f.write(brief+'\n') + if doc!='': + f.write(doc+'\n') + def write_md(self, elem, fname): @@ -205,7 +216,7 @@ def write_md(self, elem, fname): if child.tag=='class': title=child.tag+' '+child.attrib['name'] ref=child.attrib['ref'] - f.write(self.link_md(title,ref)+'\n') + f.write(self.link_md(title,ref)+'\n\n') elif child.tag=='base': self.list_bases(f,child) elif child.tag=='constructor': From d22f7004348970375909fcc040ed7d0c87c45aa1 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Sun, 16 Apr 2017 20:13:51 +0100 Subject: [PATCH 04/32] Added vc project --- cldoc.pyproj | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++ cldoc.sln | 20 ++++++++ 2 files changed, 147 insertions(+) create mode 100644 cldoc.pyproj create mode 100644 cldoc.sln diff --git a/cldoc.pyproj b/cldoc.pyproj new file mode 100644 index 0000000..24d9918 --- /dev/null +++ b/cldoc.pyproj @@ -0,0 +1,127 @@ + + + + Debug + 2.0 + {91ab4796-69d9-421c-b7a7-544a473f214b} + + scripts\cldoc-dev.py + + . + . + {888888a0-9f3d-457c-b088-3a5042f75d52} + Standard Python launcher + {2af0f10d-7135-4994-9156-5d01c9c11b7e} + 2.7 + generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1z -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-ignored-attributes -D_MSC_VER=1900 -- --type=md --output C:/Simul/documentation/ref C:/Simul/master/Simul/Base/*.h + + + False + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cldoc.sln b/cldoc.sln new file mode 100644 index 0000000..cc63a8b --- /dev/null +++ b/cldoc.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "cldoc", "cldoc.pyproj", "{91AB4796-69D9-421C-B7A7-544A473F214B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {91AB4796-69D9-421C-B7A7-544A473F214B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91AB4796-69D9-421C-B7A7-544A473F214B}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From 11476444f53ce0f38940ddbce923820076ebcbad Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Mon, 17 Apr 2017 09:56:44 +0100 Subject: [PATCH 05/32] Don't rewrite html tags. --- cldoc.pyproj | 10 ++++--- cldoc/cmdgenerate.py | 4 ++- cldoc/comment.py | 2 +- cldoc/documentmerger.py | 12 ++++---- cldoc/generators/md.py | 61 +++++++++++++++++++++++------------------ cldoc/tree.py | 2 +- 6 files changed, 53 insertions(+), 38 deletions(-) diff --git a/cldoc.pyproj b/cldoc.pyproj index 24d9918..1c19f04 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -6,17 +6,19 @@ {91ab4796-69d9-421c-b7a7-544a473f214b} scripts\cldoc-dev.py - + C:\Python27;C:\Python27\Scripts . . {888888a0-9f3d-457c-b088-3a5042f75d52} Standard Python launcher - {2af0f10d-7135-4994-9156-5d01c9c11b7e} + {9a7a9026-48c1-4688-9d5d-e5699d47d074} 2.7 - generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1z -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-ignored-attributes -D_MSC_VER=1900 -- --type=md --output C:/Simul/documentation/ref C:/Simul/master/Simul/Base/*.h + generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -- --type=md --merge C:/Simul/master/Simul/Help --output C:/Simul/documentation --md_output ref C: C:/Simul/master/Simul/Base/*.h False + + @@ -120,7 +122,7 @@ - + diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index e5acf8d..a1905b2 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -35,7 +35,6 @@ def run_generate(t, opts): generator.generate('C:\\Simul\\master\\Simul\\Help\\docout\\xml') if opts.type == 'md': generator_md = generators.Md(t, opts) - #mdout = os.path.join(baseout, 'md') generator_md.generate(baseout) if opts.type == 'html': @@ -69,6 +68,9 @@ def run(args): parser.add_argument('--output', default=None, metavar='DIR', help='specify the output directory') + parser.add_argument('--md_output', default='', metavar='DIR', + help='specify the relative markdown generated files output directory') + parser.add_argument('--language', default='c++', metavar='LANGUAGE', help='specify the default parse language (c++, c or objc)') diff --git a/cldoc/comment.py b/cldoc/comment.py index 6c3d842..25246f4 100644 --- a/cldoc/comment.py +++ b/cldoc/comment.py @@ -114,7 +114,7 @@ class UnresolvedReference(utf8.utf8): def __new__(cls, s): ns = Comment.UnresolvedReference.reescape.sub(lambda x: '\\' + x.group(0), s) - ret = utf8.utf8.__new__(cls, utf8.utf8('<{0}>').format(utf8.utf8(ns))) + ret = utf8.utf8.__new__(cls, utf8.utf8('<{0}>').format(utf8.utf8(ns))) ret.orig = s return ret diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index 81f4270..0e25370 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -8,7 +8,7 @@ class DocumentMerger: reinclude = re.compile('#') - + reheading = re.compile('(.*)\\s*{#(.*)}') def merge(self, mfilter, files): for f in files: if os.path.basename(f).startswith('.'): @@ -39,7 +39,7 @@ def _split_categories(self, filename, contents): if line == '': continue - + heading=DocumentMerger.reheading.search(line) if line.startswith(prefix) and line.endswith('>'): if len(doc) > 0 and not category: sys.stderr.write('Failed to merge file `{0}\': no # specified\n'.format(filename)) @@ -54,17 +54,19 @@ def _split_categories(self, filename, contents): doc = [] category = line[len(prefix):-1] first = True + elif heading: + category=heading.group(2) else: doc.append(line) + if not category and len(doc) > 0: + category=filename + if category: if not category in ret: ordered.append(category) ret[category] = "\n".join(doc) - elif len(doc) > 0: - sys.stderr.write('Failed to merge file `{0}\': no # specified\n'.format(filename)) - sys.exit(1) return [[c, ret[c]] for c in ordered] diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index f034654..1354805 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -62,7 +62,7 @@ def generate(self, outdir): if self.options.report: self.add_report() - self.write_md(self.index, 'index.md') + #self.write_md(self.index, 'index.md') print('Generated `{0}\''.format(outdir)) @@ -191,7 +191,10 @@ def write_md(self, elem, fname): title='Untitled' layout_name='reference' - fullpath=os.path.join(self.outdir, fname) + if(elem.tag=='category'): + fullpath=os.path.join(self.outdir, fname) + else: + fullpath=os.path.join(os.path.join(self.outdir, 'ref'),fname) self.current_path='' try: @@ -203,31 +206,37 @@ def write_md(self, elem, fname): f = fs.fs.open(fullpath, 'w') f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\n---\n') - - if elem.tag=='index': - f.write('Reference') + if(elem.tag=='category'): + f.write(elem.attrib['name']) + f.write('\n===\n') + for child in elem.getchildren(): + if child.tag=='doc': + f.write(child.text) else: - f.write(elem.tag+' ') - f.write(elem.attrib['id']) - f.write('\n===\n') - # children: - for child in elem.getchildren(): - self.indent(child) - if child.tag=='class': - title=child.tag+' '+child.attrib['name'] - ref=child.attrib['ref'] - f.write(self.link_md(title,ref)+'\n\n') - elif child.tag=='base': - self.list_bases(f,child) - elif child.tag=='constructor': - pass - elif child.tag=='method': - self.doc_method(f,child) - elif child.tag=='namespace': - title=child.tag+' '+child.attrib['name'] - ref=child.attrib['ref'] - f.write(self.link_md(title,ref)+'\n') - #tree.write(f, encoding='utf-8', xml_declaration=True) + if elem.tag=='index': + f.write('Reference') + else: + f.write(elem.tag+' ') + f.write(elem.attrib['id']) + f.write('\n===\n') + # children: + for child in elem.getchildren(): + self.indent(child) + if child.tag=='class': + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + f.write(self.link_md(title,ref)+'\n\n') + elif child.tag=='base': + self.list_bases(f,child) + elif child.tag=='constructor': + pass + elif child.tag=='method': + self.doc_method(f,child) + elif child.tag=='namespace': + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + f.write(self.link_md(title,ref)+'\n') + #tree.write(f, encoding='utf-8', xml_declaration=True) f.close() diff --git a/cldoc/tree.py b/cldoc/tree.py index 845f8ba..7be47f6 100644 --- a/cldoc/tree.py +++ b/cldoc/tree.py @@ -167,7 +167,7 @@ def process(self): if f in self.processed: continue - print('Processing {0}'.format(os.path.basename(f))) + #print('Processing {0}'.format(os.path.basename(f))) tu = self.index.parse(f, self.flags) From fc6920818f3997a31fc73e8076bb54edb3ce11de Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Mon, 17 Apr 2017 22:31:14 +0100 Subject: [PATCH 06/32] refs fixed up --- cldoc.pyproj | 6 +++--- cldoc/clang/cindex.py | 3 ++- cldoc/comment.py | 4 +++- cldoc/documentmerger.py | 29 +++++++++++++++------------- cldoc/generators/md.py | 42 ++++++++++++++++++++++++++++++----------- cldoc/generators/xml.py | 2 ++ cldoc/nodes/category.py | 8 ++++++-- cldoc/nodes/node.py | 10 +++++++++- 8 files changed, 72 insertions(+), 32 deletions(-) diff --git a/cldoc.pyproj b/cldoc.pyproj index 1c19f04..506652b 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -11,9 +11,9 @@ . {888888a0-9f3d-457c-b088-3a5042f75d52} Standard Python launcher - {9a7a9026-48c1-4688-9d5d-e5699d47d074} + {2af0f10d-7135-4994-9156-5d01c9c11b7e} 2.7 - generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -- --type=md --merge C:/Simul/master/Simul/Help --output C:/Simul/documentation --md_output ref C: C:/Simul/master/Simul/Base/*.h + generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -- --type=md --merge C:/Simul/master/Simul/Help --output C:/Simul/documentation --md_output ref C:/Simul/master/Simul/Clouds/Skylight.h False @@ -122,7 +122,7 @@ - + diff --git a/cldoc/clang/cindex.py b/cldoc/clang/cindex.py index d98ec90..eb08077 100644 --- a/cldoc/clang/cindex.py +++ b/cldoc/clang/cindex.py @@ -550,7 +550,8 @@ def name(self): @staticmethod def from_id(id): if id >= len(CursorKind._kinds) or CursorKind._kinds[id] is None: - raise ValueError,'Unknown cursor kind %d' % id + print 'Unknown cursor kind %d' % id + return CursorKind.UNEXPOSED_DECL return CursorKind._kinds[id] @staticmethod diff --git a/cldoc/comment.py b/cldoc/comment.py index 25246f4..3510bdb 100644 --- a/cldoc/comment.py +++ b/cldoc/comment.py @@ -229,8 +229,10 @@ def resolve_refs_for_doc(self, doc, resolver, root): break nds = newnds - + if len(newnds) > 0: + if refname is None: + refname=newnds[0].title components.append((newnds, refname)) else: components.append(Comment.UnresolvedReference(name)) diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index 0e25370..cfc8280 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -23,7 +23,7 @@ def _split_categories(self, filename, contents): lines = contents.splitlines() ret = {} - + title={} category = None doc = [] first = False @@ -53,26 +53,30 @@ def _split_categories(self, filename, contents): doc = [] category = line[len(prefix):-1] + this_title=category first = True elif heading: category=heading.group(2) + this_title=heading.group(1) else: doc.append(line) if not category and len(doc) > 0: - category=filename + parts=filename.replace('\\','/').replace('.md','').split('/') + category=parts[len(parts)-1] + this_title=category if category: if not category in ret: ordered.append(category) - + title[category]=this_title ret[category] = "\n".join(doc) - return [[c, ret[c]] for c in ordered] + return [[c, ret[c],title[c]] for c in ordered] def _normalized_qid(self, qid): - if qid == 'index': - return None + #if qid == 'index': + # return None if qid.startswith('::'): return qid[2:] @@ -103,21 +107,20 @@ def _merge_file(self, mfilter, filename): contents = self._read_merge_file(mfilter, filename) categories = self._split_categories(filename, contents) - for (category, docstr) in categories: + for (category, docstr, cat_title) in categories: parts = category.split('/') qid = self._normalized_qid(parts[0]) key = 'doc' - if len(parts) > 1: - key = parts[1] + #if len(parts) > 1: + # key = parts[1] if not self.qid_to_node[qid]: - self.add_categories([qid]) + self.add_categories([[qid,cat_title]]) node = self.category_to_node[qid] else: node = self.qid_to_node[qid] - if key == 'doc': node.merge_comment(comment.Comment(docstr, None), override=True) else: @@ -127,7 +130,7 @@ def _merge_file(self, mfilter, filename): def add_categories(self, categories): root = None - for category in categories: + for category,title in categories: parts = category.split('::') root = self.root @@ -149,7 +152,7 @@ def add_categories(self, categories): break if not found: - s = nodes.Category(part) + s = nodes.Category(part,title) root.append(s) root = s diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index 1354805..d6ca762 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -145,7 +145,10 @@ def argument(self,elem): ret_arg['name']=elem.attrib['name'] for child in elem.getchildren(): if child.tag=='type': - ret_arg['type']=child.attrib['name'] + if child.attrib.has_key('name'): + ret_arg['type']=child.attrib['name'] + else: + ret_arg['type']='' return ret_arg def doc_method(self,f,elem): @@ -173,6 +176,18 @@ def doc_method(self,f,elem): if doc!='': f.write(doc+'\n') + def process_text(self,elem): + res=elem.text + for child in elem.getchildren(): + if child.tag=="ref": + title=child.text + link=self.ref_to_link(child.attrib['ref']) + res+='[{0}]({1})'.format(title,link) + res+=child.tail + else: + res+=self.process_text(child) + res+=elem.tail + return res def write_md(self, elem, fname): @@ -183,7 +198,9 @@ def write_md(self, elem, fname): tree = ElementTree.ElementTree(elem) self.indent(tree.getroot()) - if elem.attrib.has_key('name'): + if elem.attrib.has_key('title'): + title=elem.attrib['title'] + elif elem.attrib.has_key('name'): title=elem.attrib['name'] elif elem.tag=='index': title='Index' @@ -191,10 +208,10 @@ def write_md(self, elem, fname): title='Untitled' layout_name='reference' - if(elem.tag=='category'): - fullpath=os.path.join(self.outdir, fname) - else: - fullpath=os.path.join(os.path.join(self.outdir, 'ref'),fname) + # if(elem.tag=='category'): + fullpath=os.path.join(self.outdir, fname) + #else: + # fullpath=os.path.join(os.path.join(self.outdir, 'ref'),fname) self.current_path='' try: @@ -207,17 +224,16 @@ def write_md(self, elem, fname): f = fs.fs.open(fullpath, 'w') f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\n---\n') if(elem.tag=='category'): - f.write(elem.attrib['name']) + f.write(title) f.write('\n===\n') for child in elem.getchildren(): if child.tag=='doc': - f.write(child.text) + f.write(self.process_text(child)) else: if elem.tag=='index': f.write('Reference') else: - f.write(elem.tag+' ') - f.write(elem.attrib['id']) + f.write(elem.tag+' '+title) f.write('\n===\n') # children: for child in elem.getchildren(): @@ -236,6 +252,8 @@ def write_md(self, elem, fname): title=child.tag+' '+child.attrib['name'] ref=child.attrib['ref'] f.write(self.link_md(title,ref)+'\n') + else: + print child.tag #tree.write(f, encoding='utf-8', xml_declaration=True) f.close() @@ -660,7 +678,9 @@ def node_to_md_ref(self, node): # Add reference item to index self.add_ref_node_id(node, elem) - + + if 'title' in props: + elem.set('title', props['title']) if 'name' in props: elem.set('name', props['name']) diff --git a/cldoc/generators/xml.py b/cldoc/generators/xml.py index 5239b89..d86c74c 100644 --- a/cldoc/generators/xml.py +++ b/cldoc/generators/xml.py @@ -533,6 +533,8 @@ def node_to_xml_ref(self, node): # Add reference item to index self.add_ref_node_id(node, elem) + if 'title' in props: + elem.set('title', props['title']) if 'name' in props: elem.set('name', props['name']) diff --git a/cldoc/nodes/category.py b/cldoc/nodes/category.py index e3445f5..c97d094 100644 --- a/cldoc/nodes/category.py +++ b/cldoc/nodes/category.py @@ -13,11 +13,15 @@ from .node import Node class Category(Node): - def __init__(self, name): + def __init__(self, name, title): Node.__init__(self, None, None) - + self._title=title self._name = name + @property + def title(self): + return self._title + @property def name(self): return self._name diff --git a/cldoc/nodes/node.py b/cldoc/nodes/node.py index 5a4b538..d0cf342 100644 --- a/cldoc/nodes/node.py +++ b/cldoc/nodes/node.py @@ -226,6 +226,10 @@ def semantic_parent(self): parent = parent.parent return parent + + @property + def title(self): + return self.name @property def qid(self): @@ -234,7 +238,10 @@ def qid(self): parent = self.semantic_parent if not parent: - return meid + if self.classname=='category': + return meid + else: + return 'ref::'+meid else: q = parent.qid @@ -255,6 +262,7 @@ def props(self): ret = { 'id': self.qid, 'name': self.name, + 'title': self.title, } if self.is_anonymous: From 596b8761833fc480d2487f5c5dd3e4a52b36d266 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Tue, 18 Apr 2017 09:46:08 +0100 Subject: [PATCH 07/32] html-style links --- cldoc.pyproj | 3 ++- cldoc/documentmerger.py | 4 ++-- cldoc/generators/md.py | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/cldoc.pyproj b/cldoc.pyproj index 506652b..585c75d 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -11,7 +11,7 @@ . {888888a0-9f3d-457c-b088-3a5042f75d52} Standard Python launcher - {2af0f10d-7135-4994-9156-5d01c9c11b7e} + {9a7a9026-48c1-4688-9d5d-e5699d47d074} 2.7 generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -- --type=md --merge C:/Simul/master/Simul/Help --output C:/Simul/documentation --md_output ref C:/Simul/master/Simul/Clouds/Skylight.h @@ -123,6 +123,7 @@ + diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index cfc8280..9469d5a 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -53,11 +53,11 @@ def _split_categories(self, filename, contents): doc = [] category = line[len(prefix):-1] - this_title=category + this_title=category.strip() first = True elif heading: category=heading.group(2) - this_title=heading.group(1) + this_title=heading.group(1).strip() else: doc.append(line) diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index d6ca762..ff6053e 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -182,7 +182,8 @@ def process_text(self,elem): if child.tag=="ref": title=child.text link=self.ref_to_link(child.attrib['ref']) - res+='[{0}]({1})'.format(title,link) + #res+='[{0}]({1})'.format(title,link) + res+='{0}'.format(title,link) res+=child.tail else: res+=self.process_text(child) From 165196d96d3420d67b71ba9d562a7245ce8dce1e Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Fri, 1 Sep 2017 11:59:43 +0100 Subject: [PATCH 08/32] Introducing doxygen-style \ commands. --- cldoc.pyproj | 9 +- cldoc/clang/README.md | 4 +- cldoc/clang/cindex-updates.patch | 80 +--- cldoc/clang/cindex.py | 557 ++++++++++++++++++++------- cldoc/cmdgenerate.py | 12 +- cldoc/comment.py | 153 +++++--- cldoc/documentmerger.py | 27 +- cldoc/generators/md.py | 257 +++++++++--- cldoc/generators/report.py | 4 +- cldoc/generators/search.py | 2 +- cldoc/generators/xml.py | 14 +- cldoc/nodes/cclass.py | 6 +- cldoc/nodes/cstruct.py | 2 +- cldoc/nodes/node.py | 23 +- cldoc/nodes/templatetypeparameter.py | 4 +- cldoc/tree.py | 37 +- scripts/cldoc-dev | 10 - 17 files changed, 830 insertions(+), 371 deletions(-) delete mode 100755 scripts/cldoc-dev diff --git a/cldoc.pyproj b/cldoc.pyproj index 585c75d..73892fd 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -11,14 +11,14 @@ . {888888a0-9f3d-457c-b088-3a5042f75d52} Standard Python launcher - {9a7a9026-48c1-4688-9d5d-e5699d47d074} + {2af0f10d-7135-4994-9156-5d01c9c11b7e} 2.7 - generate -IC:\\Simul\\master -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -- --type=md --merge C:/Simul/master/Simul/Help --output C:/Simul/documentation --md_output ref C:/Simul/master/Simul/Clouds/Skylight.h + generate -IC:\\Simul\\4.2 -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --md_output ref C:/Simul/4.2/Simul/Base/*.h C:/Simul/4.2/Simul/Math/*.h --post "c:/Simul/4.2/Simul/Help/build.bat" False - - + Path=C:\Program Files\LLVM\bin;$(Path) +also=--merge C:/Simul/4.2/Simul/Help @@ -123,7 +123,6 @@ - diff --git a/cldoc/clang/README.md b/cldoc/clang/README.md index 00154ea..0113537 100644 --- a/cldoc/clang/README.md +++ b/cldoc/clang/README.md @@ -4,8 +4,8 @@ This is an import of the python bindings for libclang taken from the `bindings/python/clang` directory of the [clang](https://github.com/llvm-mirror/clang) repository. -The files are taken from commit 8c099d9b04f0b3025e713f76b42a50f3a67d404f -(SVN commit 193725), with the modifications listed in +The files are taken from commit 54f5752c3600d39ee8de62ba9ff304154baf5e80 +(SVN commit 288149), with the modifications listed in `cldoc/clang/cindex-updates.patch`. To apply the cldoc changes, run: diff --git a/cldoc/clang/cindex-updates.patch b/cldoc/clang/cindex-updates.patch index 6217fd9..bcc23c7 100644 --- a/cldoc/clang/cindex-updates.patch +++ b/cldoc/clang/cindex-updates.patch @@ -1,5 +1,5 @@ diff --git b/cldoc/clang/cindex.py a/cldoc/clang/cindex.py -index c103c70..e5782e8 100644 +index aeb34f8..721575a 100644 --- b/cldoc/clang/cindex.py +++ a/cldoc/clang/cindex.py @@ -1,3 +1,15 @@ @@ -27,7 +27,7 @@ index c103c70..e5782e8 100644 # ctypes doesn't implicitly convert c_void_p to the appropriate wrapper # object. This is a problem, because it means that from_parameter will see an -@@ -375,6 +387,13 @@ class Diagnostic(object): +@@ -392,6 +404,13 @@ class Diagnostic(object): return conf.lib.clang_getDiagnosticOption(self, None) @property @@ -41,34 +41,16 @@ index c103c70..e5782e8 100644 def disable_option(self): """The command-line option that disables this diagnostic.""" disable = _CXString() -@@ -1130,6 +1149,12 @@ class Cursor(Structure): - """ - return conf.lib.clang_CXXMethod_isStatic(self) - -+ def is_virtual_method(self): -+ """Returns True if the cursor refers to a C++ member function or member -+ function template that is declared 'virtual'. -+ """ -+ return conf.lib.clang_CXXMethod_isVirtual(self) -+ - def get_definition(self): - """ - If the cursor is a reference to a declaration or a declaration of -@@ -1140,6 +1165,13 @@ class Cursor(Structure): - # declaration prior to issuing the lookup. - return conf.lib.clang_getCursorDefinition(self) +@@ -1136,6 +1155,8 @@ CursorKind.INCLUSION_DIRECTIVE = CursorKind(503) + CursorKind.MODULE_IMPORT_DECL = CursorKind(600) + # A type alias template declaration + CursorKind.TYPE_ALIAS_TEMPLATE_DECL = CursorKind(601) ++# A static_assert or _Static_assert node ++CursorKind.STATIC_ASSERT = CursorKind(602) -+ @property -+ def access_specifier(self): -+ if self.kind == CursorKind.CXX_BASE_SPECIFIER or self.kind == CursorKind.CXX_ACCESS_SPEC_DECL: -+ return CXXAccessSpecifier.from_value(conf.lib.clang_getCXXAccessSpecifier(self)) -+ else: -+ return None -+ - def get_usr(self): - """Return the Unified Symbol Resultion (USR) for the entity referenced - by the given cursor (or None). -@@ -1305,6 +1337,9 @@ class Cursor(Structure): + # A code completion overload candidate. + CursorKind.OVERLOAD_CANDIDATE = CursorKind(700) +@@ -1445,6 +1466,9 @@ class Cursor(Structure): return self._hash @@ -78,7 +60,7 @@ index c103c70..e5782e8 100644 @property def semantic_parent(self): """Return the semantic parent for this cursor.""" -@@ -1393,6 +1428,20 @@ class Cursor(Structure): +@@ -1576,6 +1600,20 @@ class Cursor(Structure): """ return conf.lib.clang_getFieldDeclBitWidth(self) @@ -99,41 +81,7 @@ index c103c70..e5782e8 100644 @staticmethod def from_result(res, fn, args): assert isinstance(res, Cursor) -@@ -2069,6 +2118,33 @@ class Index(ClangObject): - return TranslationUnit.from_source(path, args, unsaved_files, options, - self) - -+class CXXAccessSpecifier: -+ INVALID_ACCESS = 0 -+ PUBLIC = 1 -+ PROTECTED = 2 -+ PRIVATE = 3 -+ -+ def __init__(self, value, name): -+ self.value = value -+ self.name = name -+ -+ def __str__(self): -+ return 'CXXAccessSpecifier.' + self.name -+ -+ @staticmethod -+ def from_value(val): -+ for item in dir(CXXAccessSpecifier): -+ if item.isupper() and getattr(CXXAccessSpecifier, item) == val: -+ return CXXAccessSpecifier(val, item) -+ -+ return None -+ -+ def __cmp__(self, other): -+ return cmp(int(self), int(other)) -+ -+ def __int__(self): -+ return self.value -+ - class TranslationUnit(ClangObject): - """Represents a source code translation unit. - -@@ -2748,6 +2824,15 @@ functionList = [ +@@ -3025,6 +3063,15 @@ functionList = [ ("clang_disposeDiagnostic", [Diagnostic]), @@ -149,7 +97,7 @@ index c103c70..e5782e8 100644 ("clang_disposeIndex", [Index]), -@@ -3385,7 +3470,7 @@ class Config: +@@ -3717,7 +3764,7 @@ class Config: return True def register_enumerations(): diff --git a/cldoc/clang/cindex.py b/cldoc/clang/cindex.py index eb08077..8d1f668 100644 --- a/cldoc/clang/cindex.py +++ b/cldoc/clang/cindex.py @@ -284,7 +284,7 @@ def __contains__(self, other): return False if other.file is None and self.start.file is None: pass - elif ( self.start.file.name != other.file.name or + elif ( self.start.file.name != other.file.name or other.file.name != self.end.file.name): # same file name return False @@ -371,15 +371,32 @@ def __getitem__(self, key): return FixItIterator(self) + @property + def children(self): + class ChildDiagnosticsIterator: + def __init__(self, diag): + self.diag_set = conf.lib.clang_getChildDiagnostics(diag) + + def __len__(self): + return int(conf.lib.clang_getNumDiagnosticsInSet(self.diag_set)) + + def __getitem__(self, key): + diag = conf.lib.clang_getDiagnosticInSet(self.diag_set, key) + if not diag: + raise IndexError + return Diagnostic(diag) + + return ChildDiagnosticsIterator(self) + @property def category_number(self): - """The category number for this diagnostic.""" + """The category number for this diagnostic or 0 if unavailable.""" return conf.lib.clang_getDiagnosticCategory(self) @property def category_name(self): """The string name of the category for this diagnostic.""" - return conf.lib.clang_getDiagnosticCategoryName(self.category_number) + return conf.lib.clang_getDiagnosticCategoryText(self) @property def option(self): @@ -515,24 +532,28 @@ def register(value, name): setattr(TokenKind, name, kind) ### Cursor Kinds ### - -class CursorKind(object): - """ - A CursorKind describes the kind of entity that a cursor points to. +class BaseEnumeration(object): """ + Common base class for named enumerations held in sync with Index.h values. - # The unique kind objects, indexed by id. + Subclasses must define their own _kinds and _name_map members, as: _kinds = [] _name_map = None + These values hold the per-subclass instances and value-to-name mappings, + respectively. + + """ def __init__(self, value): - if value >= len(CursorKind._kinds): - CursorKind._kinds += [None] * (value - len(CursorKind._kinds) + 1) - if CursorKind._kinds[value] is not None: - raise ValueError,'CursorKind already loaded' + if value >= len(self.__class__._kinds): + self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) + if self.__class__._kinds[value] is not None: + raise ValueError,'{0} value {1} already loaded'.format( + str(self.__class__), value) self.value = value - CursorKind._kinds[value] = self - CursorKind._name_map = None + self.__class__._kinds[value] = self + self.__class__._name_map = None + def from_param(self): return self.value @@ -542,17 +563,29 @@ def name(self): """Get the enumeration name of this cursor kind.""" if self._name_map is None: self._name_map = {} - for key,value in CursorKind.__dict__.items(): - if isinstance(value,CursorKind): + for key, value in self.__class__.__dict__.items(): + if isinstance(value, self.__class__): self._name_map[value] = key return self._name_map[self] - @staticmethod - def from_id(id): - if id >= len(CursorKind._kinds) or CursorKind._kinds[id] is None: - print 'Unknown cursor kind %d' % id - return CursorKind.UNEXPOSED_DECL - return CursorKind._kinds[id] + @classmethod + def from_id(cls, id): + if id >= len(cls._kinds) or cls._kinds[id] is None: + raise ValueError,'Unknown template argument kind %d' % id + return cls._kinds[id] + + def __repr__(self): + return '%s.%s' % (self.__class__, self.name,) + + +class CursorKind(BaseEnumeration): + """ + A CursorKind describes the kind of entity that a cursor points to. + """ + + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None @staticmethod def get_all_kinds(): @@ -598,11 +631,6 @@ def is_unexposed(self): def __repr__(self): return 'CursorKind.%s' % (self.name,) -# FIXME: Is there a nicer way to expose this enumeration? We could potentially -# represent the nested structure, or even build a class hierarchy. The main -# things we want for sure are (a) simple external access to kinds, (b) a place -# to hang a description and name, (c) easy to keep in sync with Index.h. - ### # Declaration Kinds @@ -768,7 +796,7 @@ def __repr__(self): # that has not yet been resolved to a specific function or function template. CursorKind.OVERLOADED_DECL_REF = CursorKind(49) -# A reference to a variable that occurs in some non-expression +# A reference to a variable that occurs in some non-expression # context, e.g., a C++ lambda capture list. CursorKind.VARIABLE_REF = CursorKind(50) @@ -957,7 +985,7 @@ def __repr__(self): # Represents a C++ lambda expression that produces a local function # object. -# +# # \code # void abssort(float *x, unsigned N) { # std::sort(x, x + N, @@ -967,7 +995,7 @@ def __repr__(self): # } # \endcode CursorKind.LAMBDA_EXPR = CursorKind(144) - + # Objective-c Boolean Literal. CursorKind.OBJ_BOOL_LITERAL_EXPR = CursorKind(145) @@ -1099,6 +1127,19 @@ def __repr__(self): CursorKind.ANNOTATE_ATTR = CursorKind(406) CursorKind.ASM_LABEL_ATTR = CursorKind(407) CursorKind.PACKED_ATTR = CursorKind(408) +CursorKind.PURE_ATTR = CursorKind(409) +CursorKind.CONST_ATTR = CursorKind(410) +CursorKind.NODUPLICATE_ATTR = CursorKind(411) +CursorKind.CUDACONSTANT_ATTR = CursorKind(412) +CursorKind.CUDADEVICE_ATTR = CursorKind(413) +CursorKind.CUDAGLOBAL_ATTR = CursorKind(414) +CursorKind.CUDAHOST_ATTR = CursorKind(415) +CursorKind.CUDASHARED_ATTR = CursorKind(416) + +CursorKind.VISIBILITY_ATTR = CursorKind(417) + +CursorKind.DLLEXPORT_ATTR = CursorKind(418) +CursorKind.DLLIMPORT_ATTR = CursorKind(419) ### # Preprocessing @@ -1112,6 +1153,32 @@ def __repr__(self): # A module import declaration. CursorKind.MODULE_IMPORT_DECL = CursorKind(600) +# A type alias template declaration +CursorKind.TYPE_ALIAS_TEMPLATE_DECL = CursorKind(601) +# A static_assert or _Static_assert node +CursorKind.STATIC_ASSERT = CursorKind(602) +# A friend +CursorKind.FRIEND = CursorKind(603) + +# A code completion overload candidate. +CursorKind.OVERLOAD_CANDIDATE = CursorKind(700) + +### Template Argument Kinds ### +class TemplateArgumentKind(BaseEnumeration): + """ + A TemplateArgumentKind describes the kind of entity that a template argument + represents. + """ + + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None + +TemplateArgumentKind.NULL = TemplateArgumentKind(0) +TemplateArgumentKind.TYPE = TemplateArgumentKind(1) +TemplateArgumentKind.DECLARATION = TemplateArgumentKind(2) +TemplateArgumentKind.NULLPTR = TemplateArgumentKind(3) +TemplateArgumentKind.INTEGRAL = TemplateArgumentKind(4) ### Cursors ### @@ -1144,6 +1211,50 @@ def is_definition(self): """ return conf.lib.clang_isCursorDefinition(self) + def is_const_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'const'. + """ + return conf.lib.clang_CXXMethod_isConst(self) + + def is_converting_constructor(self): + """Returns True if the cursor refers to a C++ converting constructor. + """ + return conf.lib.clang_CXXConstructor_isConvertingConstructor(self) + + def is_copy_constructor(self): + """Returns True if the cursor refers to a C++ copy constructor. + """ + return conf.lib.clang_CXXConstructor_isCopyConstructor(self) + + def is_default_constructor(self): + """Returns True if the cursor refers to a C++ default constructor. + """ + return conf.lib.clang_CXXConstructor_isDefaultConstructor(self) + + def is_move_constructor(self): + """Returns True if the cursor refers to a C++ move constructor. + """ + return conf.lib.clang_CXXConstructor_isMoveConstructor(self) + + def is_default_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared '= default'. + """ + return conf.lib.clang_CXXMethod_isDefaulted(self) + + def is_mutable_field(self): + """Returns True if the cursor refers to a C++ field that is declared + 'mutable'. + """ + return conf.lib.clang_CXXField_isMutable(self) + + def is_pure_virtual_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared pure virtual. + """ + return conf.lib.clang_CXXMethod_isPureVirtual(self) + def is_static_method(self): """Returns True if the cursor refers to a C++ member function or member function template that is declared 'static'. @@ -1166,13 +1277,6 @@ def get_definition(self): # declaration prior to issuing the lookup. return conf.lib.clang_getCursorDefinition(self) - @property - def access_specifier(self): - if self.kind == CursorKind.CXX_BASE_SPECIFIER or self.kind == CursorKind.CXX_ACCESS_SPEC_DECL: - return CXXAccessSpecifier.from_value(conf.lib.clang_getCXXAccessSpecifier(self)) - else: - return None - def get_usr(self): """Return the Unified Symbol Resultion (USR) for the entity referenced by the given cursor (or None). @@ -1192,10 +1296,6 @@ def kind(self): @property def spelling(self): """Return the spelling of the entity pointed at by the cursor.""" - if not self.kind.is_declaration(): - # FIXME: clang_getCursorSpelling should be fixed to not assert on - # this, for consistency with clang_getCursorUSR. - return None if not hasattr(self, '_spelling'): self._spelling = conf.lib.clang_getCursorSpelling(self) @@ -1206,15 +1306,23 @@ def displayname(self): """ Return the display name for the entity referenced by this cursor. - The display name contains extra information that helps identify the cursor, - such as the parameters of a function or template or the arguments of a - class template specialization. + The display name contains extra information that helps identify the + cursor, such as the parameters of a function or template or the + arguments of a class template specialization. """ if not hasattr(self, '_displayname'): self._displayname = conf.lib.clang_getCursorDisplayName(self) return self._displayname + @property + def mangled_name(self): + """Return the mangled name for the entity referenced by this cursor.""" + if not hasattr(self, '_mangled_name'): + self._mangled_name = conf.lib.clang_Cursor_getMangling(self) + + return self._mangled_name + @property def location(self): """ @@ -1237,6 +1345,28 @@ def extent(self): return self._extent + @property + def storage_class(self): + """ + Retrieves the storage class (if any) of the entity pointed at by the + cursor. + """ + if not hasattr(self, '_storage_class'): + self._storage_class = conf.lib.clang_Cursor_getStorageClass(self) + + return StorageClass.from_id(self._storage_class) + + @property + def access_specifier(self): + """ + Retrieves the access specifier (if any) of the entity pointed at by the + cursor. + """ + if not hasattr(self, '_access_specifier'): + self._access_specifier = conf.lib.clang_getCXXAccessSpecifier(self) + + return AccessSpecifier.from_id(self._access_specifier) + @property def type(self): """ @@ -1367,7 +1497,7 @@ def translation_unit(self): @property def referenced(self): """ - For a cursor that is a reference, returns a cursor + For a cursor that is a reference, returns a cursor representing the entity that it references. """ if not hasattr(self, '_referenced'): @@ -1379,7 +1509,7 @@ def referenced(self): def brief_comment(self): """Returns the brief comment text associated with that Cursor""" return conf.lib.clang_Cursor_getBriefCommentText(self) - + @property def raw_comment(self): """Returns the raw comment text associated with that Cursor""" @@ -1391,6 +1521,27 @@ def get_arguments(self): for i in range(0, num_args): yield conf.lib.clang_Cursor_getArgument(self, i) + def get_num_template_arguments(self): + """Returns the number of template args associated with this cursor.""" + return conf.lib.clang_Cursor_getNumTemplateArguments(self) + + def get_template_argument_kind(self, num): + """Returns the TemplateArgumentKind for the indicated template + argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentKind(self, num) + + def get_template_argument_type(self, num): + """Returns the CXType for the indicated template argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentType(self, num) + + def get_template_argument_value(self, num): + """Returns the value of the indicated arg as a signed 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentValue(self, num) + + def get_template_argument_unsigned_value(self, num): + """Returns the value of the indicated arg as an unsigned 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num) + def get_children(self): """Return an iterator for accessing the children of this cursor.""" @@ -1409,6 +1560,16 @@ def visitor(child, parent, children): children) return iter(children) + def walk_preorder(self): + """Depth-first preorder walk over the cursor and its descendants. + + Yields cursors. + """ + yield self + for child in self.get_children(): + for descendant in child.walk_preorder(): + yield descendant + def get_tokens(self): """Obtain Token instances formulating that compose this Cursor. @@ -1417,6 +1578,18 @@ def get_tokens(self): """ return TokenGroup.get_tokens(self._tu, self.extent) + def get_field_offsetof(self): + """Returns the offsetof the FIELD_DECL pointed by this Cursor.""" + return conf.lib.clang_Cursor_getOffsetOfField(self) + + def is_anonymous(self): + """ + Check if the record is anonymous. + """ + if self.kind == CursorKind.FIELD_DECL: + return self.type.get_declaration().is_anonymous() + return conf.lib.clang_Cursor_isAnonymous(self) + def is_bitfield(self): """ Check if the field is a bitfield. @@ -1476,51 +1649,95 @@ def from_cursor_result(res, fn, args): res._tu = args[0]._tu return res -### Type Kinds ### - -class TypeKind(object): +class StorageClass(object): """ - Describes the kind of type. + Describes the storage class of a declaration """ - # The unique kind objects, indexed by id. + # The unique kind objects, index by id. _kinds = [] _name_map = None def __init__(self, value): - if value >= len(TypeKind._kinds): - TypeKind._kinds += [None] * (value - len(TypeKind._kinds) + 1) - if TypeKind._kinds[value] is not None: - raise ValueError,'TypeKind already loaded' + if value >= len(StorageClass._kinds): + StorageClass._kinds += [None] * (value - len(StorageClass._kinds) + 1) + if StorageClass._kinds[value] is not None: + raise ValueError,'StorageClass already loaded' self.value = value - TypeKind._kinds[value] = self - TypeKind._name_map = None + StorageClass._kinds[value] = self + StorageClass._name_map = None def from_param(self): return self.value @property def name(self): - """Get the enumeration name of this cursor kind.""" + """Get the enumeration name of this storage class.""" if self._name_map is None: self._name_map = {} - for key,value in TypeKind.__dict__.items(): - if isinstance(value,TypeKind): + for key,value in StorageClass.__dict__.items(): + if isinstance(value,StorageClass): self._name_map[value] = key return self._name_map[self] + @staticmethod + def from_id(id): + if id >= len(StorageClass._kinds) or not StorageClass._kinds[id]: + raise ValueError,'Unknown storage class %d' % id + return StorageClass._kinds[id] + + def __repr__(self): + return 'StorageClass.%s' % (self.name,) + +StorageClass.INVALID = StorageClass(0) +StorageClass.NONE = StorageClass(1) +StorageClass.EXTERN = StorageClass(2) +StorageClass.STATIC = StorageClass(3) +StorageClass.PRIVATEEXTERN = StorageClass(4) +StorageClass.OPENCLWORKGROUPLOCAL = StorageClass(5) +StorageClass.AUTO = StorageClass(6) +StorageClass.REGISTER = StorageClass(7) + + +### C++ access specifiers ### + +class AccessSpecifier(BaseEnumeration): + """ + Describes the access of a C++ class member + """ + + # The unique kind objects, index by id. + _kinds = [] + _name_map = None + + def from_param(self): + return self.value + + def __repr__(self): + return 'AccessSpecifier.%s' % (self.name,) + +AccessSpecifier.INVALID = AccessSpecifier(0) +AccessSpecifier.PUBLIC = AccessSpecifier(1) +AccessSpecifier.PROTECTED = AccessSpecifier(2) +AccessSpecifier.PRIVATE = AccessSpecifier(3) +AccessSpecifier.NONE = AccessSpecifier(4) + +### Type Kinds ### + +class TypeKind(BaseEnumeration): + """ + Describes the kind of type. + """ + + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None + @property def spelling(self): """Retrieve the spelling of this TypeKind.""" return conf.lib.clang_getTypeKindSpelling(self.value) - @staticmethod - def from_id(id): - if id >= len(TypeKind._kinds) or TypeKind._kinds[id] is None: - return TypeKind.UNEXPOSED; - #raise ValueError,'Unknown type kind %d' % id - return TypeKind._kinds[id] - def __repr__(self): return 'TypeKind.%s' % (self.name,) @@ -1554,6 +1771,7 @@ def __repr__(self): TypeKind.OBJCID = TypeKind(27) TypeKind.OBJCCLASS = TypeKind(28) TypeKind.OBJCSEL = TypeKind(29) +TypeKind.FLOAT128 = TypeKind(30) TypeKind.COMPLEX = TypeKind(100) TypeKind.POINTER = TypeKind(101) TypeKind.BLOCKPOINTER = TypeKind(102) @@ -1572,44 +1790,19 @@ def __repr__(self): TypeKind.VARIABLEARRAY = TypeKind(115) TypeKind.DEPENDENTSIZEDARRAY = TypeKind(116) TypeKind.MEMBERPOINTER = TypeKind(117) +TypeKind.AUTO = TypeKind(118) +TypeKind.ELABORATED = TypeKind(119) -class RefQualifierKind(object): +class RefQualifierKind(BaseEnumeration): """Describes a specific ref-qualifier of a type.""" # The unique kind objects, indexed by id. _kinds = [] _name_map = None - def __init__(self, value): - if value >= len(RefQualifierKind._kinds): - num_kinds = value - len(RefQualifierKind._kinds) + 1 - RefQualifierKind._kinds += [None] * num_kinds - if RefQualifierKind._kinds[value] is not None: - raise ValueError, 'RefQualifierKind already loaded' - self.value = value - RefQualifierKind._kinds[value] = self - RefQualifierKind._name_map = None - def from_param(self): return self.value - @property - def name(self): - """Get the enumeration name of this kind.""" - if self._name_map is None: - self._name_map = {} - for key, value in RefQualifierKind.__dict__.items(): - if isinstance(value, RefQualifierKind): - self._name_map[value] = key - return self._name_map[self] - - @staticmethod - def from_id(id): - if (id >= len(RefQualifierKind._kinds) or - RefQualifierKind._kinds[id] is None): - raise ValueError, 'Unknown type kind %d' % id - return RefQualifierKind._kinds[id] - def __repr__(self): return 'RefQualifierKind.%s' % (self.name,) @@ -1797,6 +1990,12 @@ def get_class_type(self): """ return conf.lib.clang_Type_getClassType(self) + def get_named_type(self): + """ + Retrieve the type named by the qualified-id. + """ + return conf.lib.clang_Type_getNamedType(self) + def get_align(self): """ Retrieve the alignment of the record. @@ -1822,6 +2021,21 @@ def get_ref_qualifier(self): return RefQualifierKind.from_id( conf.lib.clang_Type_getCXXRefQualifier(self)) + def get_fields(self): + """Return an iterator for accessing the fields of this type.""" + + def visitor(field, children): + assert field != conf.lib.clang_getNullCursor() + + # Create reference to TU so it isn't GC'd before Cursor. + field._tu = self._tu + fields.append(field) + return 1 # continue + fields = [] + conf.lib.clang_Type_visitFields(self, + callbacks['fields_visit'](visitor), fields) + return iter(fields) + @property def spelling(self): """Retrieve the spelling of this Type.""" @@ -1871,7 +2085,7 @@ class _CXUnsavedFile(Structure): # 5 : CompletionChunk.Kind("CurrentParameter"), 6: '(', # CompletionChunk.Kind("LeftParen"), 7: ')', # CompletionChunk.Kind("RightParen"), - 8: ']', # CompletionChunk.Kind("LeftBracket"), + 8: '[', # CompletionChunk.Kind("LeftBracket"), 9: ']', # CompletionChunk.Kind("RightBracket"), 10: '{', # CompletionChunk.Kind("LeftBrace"), 11: '}', # CompletionChunk.Kind("RightBrace"), @@ -1985,7 +2199,7 @@ def __repr__(self): return "" % self def __len__(self): - self.num_chunks + return self.num_chunks @CachedProperty def num_chunks(self): @@ -2120,33 +2334,6 @@ def parse(self, path, args=None, unsaved_files=None, options = 0): return TranslationUnit.from_source(path, args, unsaved_files, options, self) -class CXXAccessSpecifier: - INVALID_ACCESS = 0 - PUBLIC = 1 - PROTECTED = 2 - PRIVATE = 3 - - def __init__(self, value, name): - self.value = value - self.name = name - - def __str__(self): - return 'CXXAccessSpecifier.' + self.name - - @staticmethod - def from_value(val): - for item in dir(CXXAccessSpecifier): - if item.isupper() and getattr(CXXAccessSpecifier, item) == val: - return CXXAccessSpecifier(val, item) - - return None - - def __cmp__(self, other): - return cmp(int(self), int(other)) - - def __int__(self): - return self.value - class TranslationUnit(ClangObject): """Represents a source code translation unit. @@ -2290,7 +2477,7 @@ def __init__(self, ptr, index): functions above. __init__ is only called internally. """ assert isinstance(index, Index) - + self.index = index ClangObject.__init__(self, ptr) def __del__(self): @@ -2381,6 +2568,9 @@ def get_extent(self, filename, locations): end_location[0], end_location[1]) elif isinstance(end_location, int): end_location = SourceLocation.from_offset(self, f, end_location) + else: + end_location = SourceLocation.from_offset(self, f, int(end_location)) + assert isinstance(start_location, SourceLocation) assert isinstance(end_location, SourceLocation) @@ -2579,7 +2769,7 @@ class CompilationDatabaseError(Exception): constants in this class. """ - # An unknown error occured + # An unknown error occurred ERROR_UNKNOWN = 0 # The database could not be loaded @@ -2609,6 +2799,11 @@ def directory(self): """Get the working directory for this CompileCommand""" return conf.lib.clang_CompileCommand_getDirectory(self.cmd) + @property + def filename(self): + """Get the working filename for this CompileCommand""" + return conf.lib.clang_CompileCommand_getFilename(self.cmd) + @property def arguments(self): """ @@ -2685,6 +2880,14 @@ def getCompileCommands(self, filename): return conf.lib.clang_CompilationDatabase_getCompileCommands(self, filename) + def getAllCompileCommands(self): + """ + Get an iterable object providing all the CompileCommands available from + the database. + """ + return conf.lib.clang_CompilationDatabase_getAllCompileCommands(self) + + class Token(Structure): """Represents a single token from the preprocessor. @@ -2737,6 +2940,7 @@ def cursor(self): callbacks['translation_unit_includes'] = CFUNCTYPE(None, c_object_p, POINTER(SourceLocation), c_uint, py_object) callbacks['cursor_visit'] = CFUNCTYPE(c_int, Cursor, Cursor, py_object) +callbacks['fields_visit'] = CFUNCTYPE(c_int, Cursor, py_object) # Functions strictly alphabetical order. functionList = [ @@ -2751,6 +2955,11 @@ def cursor(self): c_object_p, CompilationDatabase.from_result), + ("clang_CompilationDatabase_getAllCompileCommands", + [c_object_p], + c_object_p, + CompileCommands.from_result), + ("clang_CompilationDatabase_getCompileCommands", [c_object_p, c_char_p], c_object_p, @@ -2777,6 +2986,11 @@ def cursor(self): _CXString, _CXString.from_result), + ("clang_CompileCommand_getFilename", + [c_object_p], + _CXString, + _CXString.from_result), + ("clang_CompileCommand_getNumArgs", [c_object_p], c_uint), @@ -2801,6 +3015,34 @@ def cursor(self): [Index, c_char_p], c_object_p), + ("clang_CXXConstructor_isConvertingConstructor", + [Cursor], + bool), + + ("clang_CXXConstructor_isCopyConstructor", + [Cursor], + bool), + + ("clang_CXXConstructor_isDefaultConstructor", + [Cursor], + bool), + + ("clang_CXXConstructor_isMoveConstructor", + [Cursor], + bool), + + ("clang_CXXField_isMutable", + [Cursor], + bool), + + ("clang_CXXMethod_isConst", + [Cursor], + bool), + + ("clang_CXXMethod_isDefaulted", + [Cursor], + bool), + ("clang_CXXMethod_isPureVirtual", [Cursor], bool), @@ -2891,6 +3133,10 @@ def cursor(self): Type, Type.from_result), + ("clang_getChildDiagnostics", + [Diagnostic], + c_object_p), + ("clang_getCompletionAvailability", [c_void_p], c_int), @@ -2975,6 +3221,11 @@ def cursor(self): _CXString, _CXString.from_result), + ("clang_Cursor_getMangling", + [Cursor], + _CXString, + _CXString.from_result), + # ("clang_getCXTUResourceUsage", # [TranslationUnit], # CXTUResourceUsage), @@ -2996,8 +3247,8 @@ def cursor(self): [Diagnostic], c_uint), - ("clang_getDiagnosticCategoryName", - [c_uint], + ("clang_getDiagnosticCategoryText", + [Diagnostic], _CXString, _CXString.from_result), @@ -3006,6 +3257,10 @@ def cursor(self): _CXString, _CXString.from_result), + ("clang_getDiagnosticInSet", + [c_object_p, c_uint], + c_object_p), + ("clang_getDiagnosticLocation", [Diagnostic], SourceLocation), @@ -3107,6 +3362,10 @@ def cursor(self): [c_object_p], c_uint), + ("clang_getNumDiagnosticsInSet", + [c_object_p], + c_uint), + ("clang_getNumElements", [Type], c_longlong), @@ -3302,6 +3561,31 @@ def cursor(self): Cursor, Cursor.from_result), + ("clang_Cursor_getNumTemplateArguments", + [Cursor], + c_int), + + ("clang_Cursor_getTemplateArgumentKind", + [Cursor, c_uint], + TemplateArgumentKind.from_id), + + ("clang_Cursor_getTemplateArgumentType", + [Cursor, c_uint], + Type, + Type.from_result), + + ("clang_Cursor_getTemplateArgumentValue", + [Cursor, c_uint], + c_longlong), + + ("clang_Cursor_getTemplateArgumentUnsignedValue", + [Cursor, c_uint], + c_ulonglong), + + ("clang_Cursor_isAnonymous", + [Cursor], + bool), + ("clang_Cursor_isBitField", [Cursor], bool), @@ -3316,6 +3600,10 @@ def cursor(self): _CXString, _CXString.from_result), + ("clang_Cursor_getOffsetOfField", + [Cursor], + c_longlong), + ("clang_Type_getAlignOf", [Type], c_longlong), @@ -3336,6 +3624,15 @@ def cursor(self): ("clang_Type_getCXXRefQualifier", [Type], c_uint), + + ("clang_Type_getNamedType", + [Type], + Type, + Type.from_result), + + ("clang_Type_visitFields", + [Type, callbacks['fields_visit'], py_object], + c_uint), ] class LibclangError(Exception): @@ -3416,8 +3713,8 @@ def set_compatibility_check(check_status): python bindings can disable the compatibility check. This will cause the python bindings to load, even though they are written for a newer version of libclang. Failures now arise if unsupported or incompatible - features are accessed. The user is required to test himself if the - features he is using are available and compatible between different + features are accessed. The user is required to test themselves if the + features they are using are available and compatible between different libclang versions. """ if Config.loaded: diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index a1905b2..3ce562d 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -24,15 +24,15 @@ def run_generate(t, opts): from . import generators - generator = generators.Xml(t, opts) - if opts.type == 'html' and opts.static: baseout = fs.fs.mkdtemp() else: baseout = opts.output xmlout = os.path.join(baseout, 'xml') - generator.generate('C:\\Simul\\master\\Simul\\Help\\docout\\xml') + if opts.type == 'xml': + generator = generators.Xml(t, opts) + generator.generate('C:\\Simul\\master\\Simul\\Help\\docout\\xml') if opts.type == 'md': generator_md = generators.Md(t, opts) generator_md.generate(baseout) @@ -42,6 +42,9 @@ def run_generate(t, opts): if opts.static: staticsite.generate(baseout, opts) + if opts.post != '': + args=opts.post.split(' ') + subprocess.call(args,shell=True) def run(args): try: @@ -94,6 +97,9 @@ def run(args): parser.add_argument('--custom-css', default=[], metavar='FILES', action='append', help='specify additional css files to be merged into the html (only for when --output is html)') + + parser.add_argument('--post', default=None, metavar='POST', + help='command to execute after completion') parser.add_argument('files', nargs='+', help='files to parse') diff --git a/cldoc/comment.py b/cldoc/comment.py index 3510bdb..bcd78e5 100644 --- a/cldoc/comment.py +++ b/cldoc/comment.py @@ -120,9 +120,12 @@ def __new__(cls, s): return ret redocref = re.compile('(?P[$]?)<(?:\\[(?P[^\\]]*)\\])?(?Poperator(?:>>|>|>=)|[^>\n]+)>') - redoccode = re.compile('^ \\[code\\]\n(?P(?:(?: .*|)\n)*)', re.M) + redoccode = re.compile('\\\\code(.*\\n?)\\\\endcode', re.M | re.S) + #redoccode = re.compile('^ \\[code\\]\n(?P(?:(?: .*|)\n)*)', re.M) redocmcode = re.compile('(^ *(`{3,}|~{3,}).*?\\2)', re.M | re.S) + re_dox_ref=re.compile('\\\\(?P[a-zA-Z]+)\\s+(?P\\w+)(?:\\s+"(?P\\w+)")?') + def __init__(self, text, location): self.__dict__['docstrings'] = [] self.__dict__['text'] = text @@ -176,29 +179,52 @@ def redoc_split(self, doc): for c in components: if isinstance(c, Comment.Example) or isinstance(c, Comment.MarkdownCode): - ret.append((c, None, None)) + ret.append((c, None, None, None)) else: lastpos = 0 - - for m in Comment.redocref.finditer(c): - span = m.span(0) - - prefix = c[lastpos:span[0]] - lastpos = span[1] - - ref = m.group('ref') - refname = m.group('refname') - - if not refname: - refname = None - - if len(m.group('isregex')) > 0: - ref = re.compile(ref) - - ret.append((prefix, ref, refname)) - - ret.append((c[lastpos:], None, None)) - + pos=0 + while pos 0: + ref = re.compile(ref) + + elif dox_ref and min_pos==dox_ref.start(): + m=dox_ref + span = m.span(0) + prefix = c[lastpos:span[0]] + lastpos = span[1] + try: + command = m.group('command') + ref = m.group('ref') + refname = m.group('refname') + except: + pass + else: + prefix=c[lastpos:] + lastpos=len(c) + ret.append((prefix,command,ref, refname)) + pos=lastpos return ret def resolve_refs_for_doc(self, doc, resolver, root): @@ -206,36 +232,46 @@ def resolve_refs_for_doc(self, doc, resolver, root): components = [] for pair in comps: - prefix, name, refname = pair + prefix, command, name, refname = pair components.append(prefix) - - if name is None: - continue - - if isinstance(name, utf8.string): - names = name.split('::') - else: - names = [name] - - nds = [root] - - for j in range(len(names)): - newnds = [] - - for n in nds: - newnds += resolver(n, names[j], j == 0) - - if len(newnds) == 0: - break - - nds = newnds - - if len(newnds) > 0: - if refname is None: - refname=newnds[0].title - components.append((newnds, refname)) + if command==None: + pass + elif command=='ref': + if name is None: + continue + + if isinstance(name, utf8.string): + names = name.split('::') + else: + names = [name] + + nds = [root] + + for j in range(len(names)): + newnds = [] + + for n in nds: + newnds += resolver(n, names[j], j == 0) + + if len(newnds) == 0: + break + + nds = newnds + + if len(newnds) > 0: + components.append((newnds, refname)) + else: + components.append(Comment.UnresolvedReference(name)) + elif command=='a': + components.append('**'+name+'**') + elif command=='param': + components.append('\n**'+name+'**') + elif command=='return': + components.append('\n**return:** '+name) + elif command=='brief': + pass else: - components.append(Comment.UnresolvedReference(name)) + print(self.location.file.name+' ('+str(self.location.line)+'): warning: Unknown command \\'+ command) doc.components = components @@ -436,17 +472,22 @@ def extract_loop(self, iter): def clean(self, token): prelen = token.extent.start.column - 1 comment = token.spelling.strip() - - if comment.startswith('//'): - if len(comment) > 2 and comment[2] == '-': - return None + + if comment.startswith('///') or comment.startswith('//!'): + return comment[3:].strip() + elif comment.startswith('//'): + # For our purposes, ordinary comments are ignored. + return None + #if len(comment) > 2 and comment[2] == '-': + # return None return comment[2:].strip() elif comment.startswith('/*') and comment.endswith('*/'): - if comment[2] == '-': + # For our purposes, ! is required here. + if comment[2] != '!': return None - lines = comment[2:-2].splitlines() + lines = comment[3:-2].splitlines() if len(lines) == 1 and len(lines[0]) > 0 and lines[0][0] == ' ': return lines[0][1:].rstrip() diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index 9469d5a..908709c 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -8,7 +8,7 @@ class DocumentMerger: reinclude = re.compile('#') - reheading = re.compile('(.*)\\s*{#(.*)}') + reheading = re.compile('(.*)\\s*{#(?:([0-9]*):)?(.*)}') def merge(self, mfilter, files): for f in files: if os.path.basename(f).startswith('.'): @@ -28,6 +28,8 @@ def _split_categories(self, filename, contents): doc = [] first = False ordered = [] + weight = {} + this_weight=0 for line in lines: prefix = '# 0: parts=filename.replace('\\','/').replace('.md','').split('/') category=parts[len(parts)-1] @@ -71,8 +76,9 @@ def _split_categories(self, filename, contents): ordered.append(category) title[category]=this_title ret[category] = "\n".join(doc) + weight[category] = this_weight - return [[c, ret[c],title[c]] for c in ordered] + return [[c, ret[c],title[c],weight[c]] for c in ordered] def _normalized_qid(self, qid): #if qid == 'index': @@ -107,7 +113,17 @@ def _merge_file(self, mfilter, filename): contents = self._read_merge_file(mfilter, filename) categories = self._split_categories(filename, contents) - for (category, docstr, cat_title) in categories: + for (category, docstr, cat_title, weight) in categories: + # First, split off any order number from the front e.g. 3:name + category=category.replace('::','_DOUBLECOLONSEPARATOR_') + front_back= category.split(':') + front='' + if len(front_back)>1: + category=front_back[1] + front=front_back[0] + else: + category=front_back[0] + category=category.replace('_DOUBLECOLONSEPARATOR_','::') parts = category.split('/') qid = self._normalized_qid(parts[0]) @@ -121,6 +137,7 @@ def _merge_file(self, mfilter, filename): node = self.category_to_node[qid] else: node = self.qid_to_node[qid] + node.weight=weight if key == 'doc': node.merge_comment(comment.Comment(docstr, None), override=True) else: diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index ff6053e..9eca3d4 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -28,6 +28,7 @@ def __init__(self, tree=None, opts=None): self.tree = tree self.options = opts self.namespaces_as_directories=True + self._refid=None def generate(self, outdir): if not outdir: @@ -66,6 +67,7 @@ def generate(self, outdir): print('Generated `{0}\''.format(outdir)) + def add_report(self): from .report import Report @@ -132,7 +134,32 @@ def list_bases(self,f,elem): ref=child.attrib['ref'] f.write(self.link_md(title,ref)) f.write('\n') - + + def get_return_type(self,elem): + ret_type='' + for child in elem.getchildren(): + if child.tag=='return': + for c in child.getchildren(): + if c.tag=='type': + ret_type+=c.attrib['name'] + if c.attrib.has_key('qualifier'): + ret_type=c.attrib['qualifier']+' '; + return ret_type + + def get_brief(self,elem): + brief='' + for child in elem.getchildren(): + if child.tag=='brief': + brief=child.text + return brief + + def get_doc(self,elem): + doc='' + for child in elem.getchildren(): + if child.tag=='doc': + doc=child.text + return doc + def return_type(self,elem): ret_type=dict() for child in elem.getchildren(): @@ -151,31 +178,58 @@ def argument(self,elem): ret_arg['type']='' return ret_arg - def doc_method(self,f,elem): + def get_arguments(self,elem): arguments=[] - ret_type=dict() + for child in elem.getchildren(): + if child.tag=='argument': + arguments.append(self.argument(child)) + return arguments + + def get_arguments_text(self,elem): + arguments=self.get_arguments(elem) + args_txt=[] + for arg in arguments: + args_txt.append(arg['name']) + arglist=','.join(args_txt) + return arglist + + def get_typed_arguments_text(self,elem): + arguments=self.get_arguments(elem) + args_txt=[] + for arg in arguments: + tp_parts=[] + if arg['type']!='': + tp_parts.append(arg['type']) + if arg['name']!='': + tp_parts.append(arg['name']) + tp=' '.join(tp_parts) + args_txt.append(tp) + arglist=', '.join(args_txt) + return arglist + + def doc_method(self,f,elem): + ret_type='' doc='' brief='' for child in elem.getchildren(): if child.tag=='return': - ret_type=self.return_type(child) - elif child.tag=='argument': - arguments.append(self.argument(child)) + ret_type=self.get_return_type(elem) elif child.tag=='brief': brief=child.text elif child.tag=='doc': doc=child.text - f.write(ret_type['type']+' '+elem.attrib['name']) - args_txt=[] - for arg in arguments: - args_txt.append(arg['name']) - f.write('('+','.join(args_txt)+')') - f.write('\n------\n\n') + #blank line before a heading h4: + f.write('\n### '+ret_type+' '+elem.attrib['name']) + + arglist=self.get_typed_arguments_text(elem) + f.write('('+arglist+')') + f.write('\n') if brief!='': f.write(brief+'\n') if doc!='': f.write(doc+'\n') + def process_text(self,elem): res=elem.text for child in elem.getchildren(): @@ -207,6 +261,10 @@ def write_md(self, elem, fname): title='Index' else: title='Untitled' + + weight=0 + if elem.attrib.has_key('weight'): + weight=elem.attrib['weight'] layout_name='reference' # if(elem.tag=='category'): @@ -223,7 +281,7 @@ def write_md(self, elem, fname): pass f = fs.fs.open(fullpath, 'w') - f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\n---\n') + f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\nweight: '+str(weight)+'\n---\n') if(elem.tag=='category'): f.write(title) f.write('\n===\n') @@ -235,24 +293,90 @@ def write_md(self, elem, fname): f.write('Reference') else: f.write(elem.tag+' '+title) - f.write('\n===\n') + f.write('\n===\n\n') + + brief=self.get_brief(elem) + doc=self.get_doc(elem) + + if brief=='': + brief=doc + if doc=='': + doc=brief + if brief: + f.write(brief+'\n') + + # method declarations + f.write('\n') + any_methods=False + any_namespaces=False + any_classes=False + any_bases=False + + for child in elem.getchildren(): + if child.tag!='base': + continue + any_bases=True + + for child in elem.getchildren(): + if child.tag!='method': + continue + f.write('\n| '+self.get_return_type(child)+' | ['+child.attrib['name']+'](#'+child.attrib['name']+')('+self.get_typed_arguments_text(child)+') |') + any_methods=True + + for child in elem.getchildren(): + if child.tag!='namespace': + continue + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + lnk=self.ref_to_link(ref) + #f.write(self.link_md(title,ref)+'\n') + br=self.get_brief(child) + f.write('\n| ['+title+']('+lnk+') | '+br+' |') + any_namespaces=True + + for child in elem.getchildren(): + if child.tag!='class': + continue + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + lnk=self.ref_to_link(ref) + br=self.get_brief(child) + f.write('\n| ['+title+']('+lnk+') | '+br+' |') + any_classes=True + + f.write('\n') + # main text + if doc: + f.write('\n'+doc+'\n') + f.write('\n') + + if any_bases: + f.write('Base Classes\n---\n') + for child in elem.getchildren(): + self.indent(child) + if child.tag=='base': + self.list_bases(f,child) + + if any_methods: + f.write('Member Functions\n---\n') + for child in elem.getchildren(): + self.indent(child) + if child.tag=='method': + self.doc_method(f,child) + # children: for child in elem.getchildren(): self.indent(child) - if child.tag=='class': - title=child.tag+' '+child.attrib['name'] - ref=child.attrib['ref'] - f.write(self.link_md(title,ref)+'\n\n') - elif child.tag=='base': + if child.tag=='base': self.list_bases(f,child) + elif child.tag=='namespace': + pass elif child.tag=='constructor': pass + elif child.tag=='class': + pass elif child.tag=='method': - self.doc_method(f,child) - elif child.tag=='namespace': - title=child.tag+' '+child.attrib['name'] - ref=child.attrib['ref'] - f.write(self.link_md(title,ref)+'\n') + pass else: print child.tag #tree.write(f, encoding='utf-8', xml_declaration=True) @@ -296,8 +420,11 @@ def is_top(self, node): return False def refid(self, node): - if not node._refid is None: - return node._refid + try: + if not node._refid is None: + return node._refid + except: + return '' parent = node @@ -335,25 +462,25 @@ def add_ref_id(self, cursor, elem): self.add_ref_node_id(node, elem) - def type_to_xml(self, tp, parent=None): + def type_to_md(self, tp, parent=None): elem = ElementTree.Element('type') if tp.is_constant_array: elem.set('size', str(tp.constant_array_size)) elem.set('class', 'array') - elem.append(self.type_to_xml(tp.element_type, parent)) + elem.append(self.type_to_md(tp.element_type, parent)) elif tp.is_function: elem.set('class', 'function') result = ElementTree.Element('result') - result.append(self.type_to_xml(tp.function_result, parent)) + result.append(self.type_to_md(tp.function_result, parent)) elem.append(result) args = ElementTree.Element('arguments') elem.append(args) for arg in tp.function_arguments: - args.append(self.type_to_xml(arg, parent)) + args.append(self.type_to_md(arg, parent)) else: elem.set('name', tp.typename_for(parent)) @@ -375,35 +502,35 @@ def type_to_xml(self, tp, parent=None): self.add_ref_id(tp.decl, elem) return elem - def enumvalue_to_xml(self, node, elem): + def enumvalue_to_md(self, node, elem): elem.set('value', str(node.value)) - def enum_to_xml(self, node, elem): + def enum_to_md(self, node, elem): if not node.typedef is None: elem.set('typedef', 'yes') if node.isclass: elem.set('class', 'yes') - def struct_to_xml(self, node, elem): - self.class_to_xml(node, elem) + def struct_to_md(self, node, elem): + self.class_to_md(node, elem) if not node.typedef is None: elem.set('typedef', 'yes') - def templatetypeparameter_to_xml(self, node, elem): + def templatetypeparameter_to_md(self, node, elem): dt = node.default_type if not dt is None: d = ElementTree.Element('default') - d.append(self.type_to_xml(dt)) + d.append(self.type_to_md(dt)) elem.append(d) - def templatenontypeparameter_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type)) + def templatenontypeparameter_to_md(self, node, elem): + elem.append(self.type_to_md(node.type)) - def function_to_xml(self, node, elem): + def function_to_md(self, node, elem): if not (isinstance(node, nodes.Constructor) or isinstance(node, nodes.Destructor)): ret = ElementTree.Element('return') @@ -411,7 +538,7 @@ def function_to_xml(self, node, elem): if not node.comment is None and hasattr(node.comment, 'returns') and node.comment.returns: ret.append(self.doc_to_md(node, node.comment.returns)) - tp = self.type_to_xml(node.return_type, node.parent) + tp = self.type_to_md(node.return_type, node.parent) ret.append(tp) elem.append(ret) @@ -424,11 +551,11 @@ def function_to_xml(self, node, elem): if not node.comment is None and arg.name in node.comment.params: ret.append(self.doc_to_md(node, node.comment.params[arg.name])) - ret.append(self.type_to_xml(arg.type, node.parent)) + ret.append(self.type_to_md(arg.type, node.parent)) elem.append(ret) - def method_to_xml(self, node, elem): - self.function_to_xml(node, elem) + def method_to_md(self, node, elem): + self.function_to_md(node, elem) if len(node.override) > 0: elem.set('override', 'yes') @@ -450,24 +577,24 @@ def method_to_xml(self, node, elem): if node.abstract: elem.set('abstract', 'yes') - def typedef_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node)) + def typedef_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node)) - def typedef_to_xml_ref(self, node, elem): - elem.append(self.type_to_xml(node.type, node)) + def typedef_to_md_ref(self, node, elem): + elem.append(self.type_to_md(node.type, node)) - def variable_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node.parent)) + def variable_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node.parent)) - def property_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node.parent)) + def property_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node.parent)) def set_access_attribute(self, node, elem): - if node.access == cindex.CXXAccessSpecifier.PROTECTED: + if node.access == cindex.AccessSpecifier.PROTECTED: elem.set('access', 'protected') - elif node.access == cindex.CXXAccessSpecifier.PRIVATE: + elif node.access == cindex.AccessSpecifier.PRIVATE: elem.set('access', 'private') - elif node.access == cindex.CXXAccessSpecifier.PUBLIC: + elif node.access == cindex.AccessSpecifier.PUBLIC: elem.set('access', 'public') def process_bases(self, node, elem, bases, tagname): @@ -476,7 +603,7 @@ def process_bases(self, node, elem, bases, tagname): self.set_access_attribute(base, child) - child.append(self.type_to_xml(base.type, node)) + child.append(self.type_to_md(base.type, node)) if base.node and not base.node.comment is None and base.node.comment.brief: child.append(self.doc_to_md(base.node, base.node.comment.brief, 'brief')) @@ -497,7 +624,7 @@ def process_subclasses(self, node, elem, subclasses, tagname): elem.append(child) - def class_to_xml(self, node, elem): + def class_to_md(self, node, elem): self.process_bases(node, elem, node.bases, 'base') self.process_bases(node, elem, node.implements, 'implements') @@ -519,8 +646,8 @@ def class_to_xml(self, node, elem): else: elem.set('abstract', 'true') - def field_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node.parent)) + def field_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node.parent)) def doc_to_md(self, parent, doc, tagname='doc'): doce = ElementTree.Element(tagname) @@ -642,10 +769,10 @@ def node_to_md(self, node): if not node.comment is None and node.comment.doc: elem.append(self.doc_to_md(node, node.comment.doc)) - self.call_type_specific(node, elem, 'to_xml') + self.call_type_specific(node, elem, 'to_md') for child in node.sorted_children(): - if child.access == cindex.CXXAccessSpecifier.PRIVATE: + if child.access == cindex.AccessSpecifier.PRIVATE: continue self.refid(child) @@ -659,7 +786,7 @@ def node_to_md(self, node): return elem - def templated_to_xml_ref(self, node, element): + def templated_to_md_ref(self, node, element): for child in node.sorted_children(): if not (isinstance(child, nodes.TemplateTypeParameter) or isinstance(child, nodes.TemplateNonTypeParameter)): continue @@ -671,7 +798,7 @@ def generate_page(self, node): self.namespace_separator='.' if self.namespaces_as_directories==True: self.namespace_separator='/' - self.write_md(elem, node.qid.replace('::', self.namespace_separator) + '.md') + self.write_md(elem, node.output_filename(self.namespace_separator) + '.md') def node_to_md_ref(self, node): elem = ElementTree.Element(node.classname) @@ -684,17 +811,19 @@ def node_to_md_ref(self, node): elem.set('title', props['title']) if 'name' in props: elem.set('name', props['name']) + if 'weight' in props: + elem.set('weight', props['weight']) if not node.comment is None and node.comment.brief: elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) - self.call_type_specific(node, elem, 'to_xml_ref') + self.call_type_specific(node, elem, 'to_md_ref') return elem def generate_node(self, node): # Ignore private stuff - if node.access == cindex.CXXAccessSpecifier.PRIVATE: + if node.access == cindex.AccessSpecifier.PRIVATE: return self.refid(node) diff --git a/cldoc/generators/report.py b/cldoc/generators/report.py index b4eb2cc..282fbf6 100644 --- a/cldoc/generators/report.py +++ b/cldoc/generators/report.py @@ -69,7 +69,7 @@ def arguments(self, root): if not isinstance(node, nodes.Function): continue - if node.access == cindex.CXXAccessSpecifier.PRIVATE: + if node.access == cindex.AccessSpecifier.PRIVATE: continue if node.comment is None: @@ -138,7 +138,7 @@ def coverage(self, root): for node in self.tree.all_nodes: cname = node.__class__.__name__ - if node.access == cindex.CXXAccessSpecifier.PRIVATE: + if node.access == cindex.AccessSpecifier.PRIVATE: continue if not cname in pertype: diff --git a/cldoc/generators/search.py b/cldoc/generators/search.py index 07b5485..6732941 100644 --- a/cldoc/generators/search.py +++ b/cldoc/generators/search.py @@ -25,7 +25,7 @@ def __init__(self, tree): self.db = [] for node in tree.root.descendants(): - if not node._refid is None and node.access != cindex.CXXAccessSpecifier.PRIVATE: + if not node._refid is None and node.access != cindex.AccessSpecifier.PRIVATE: self.make_index(node) def make_index(self, node): diff --git a/cldoc/generators/xml.py b/cldoc/generators/xml.py index d86c74c..da7d8f0 100644 --- a/cldoc/generators/xml.py +++ b/cldoc/generators/xml.py @@ -108,7 +108,7 @@ def write_xml(self, elem, fname): tree = ElementTree.ElementTree(elem) self.indent(tree.getroot()) - + f = fs.fs.open(os.path.join(self.outdir, fname), 'w') tree.write(f, encoding='utf-8', xml_declaration=True) f.write('\n') @@ -319,11 +319,11 @@ def property_to_xml(self, node, elem): elem.append(self.type_to_xml(node.type, node.parent)) def set_access_attribute(self, node, elem): - if node.access == cindex.CXXAccessSpecifier.PROTECTED: + if node.access == cindex.AccessSpecifier.PROTECTED: elem.set('access', 'protected') - elif node.access == cindex.CXXAccessSpecifier.PRIVATE: + elif node.access == cindex.AccessSpecifier.PRIVATE: elem.set('access', 'private') - elif node.access == cindex.CXXAccessSpecifier.PUBLIC: + elif node.access == cindex.AccessSpecifier.PUBLIC: elem.set('access', 'public') def process_bases(self, node, elem, bases, tagname): @@ -501,7 +501,7 @@ def node_to_xml(self, node): self.call_type_specific(node, elem, 'to_xml') for child in node.sorted_children(): - if child.access == cindex.CXXAccessSpecifier.PRIVATE: + if child.access == cindex.AccessSpecifier.PRIVATE: continue self.refid(child) @@ -524,7 +524,7 @@ def templated_to_xml_ref(self, node, element): def generate_page(self, node): elem = self.node_to_xml(node) - self.write_xml(elem, node.qid.replace('::', '.') + '.xml') + self.write_xml(elem, node.qid.replace('::', '.').replace(':', '.') + '.xml') def node_to_xml_ref(self, node): elem = ElementTree.Element(node.classname) @@ -547,7 +547,7 @@ def node_to_xml_ref(self, node): def generate_node(self, node): # Ignore private stuff - if node.access == cindex.CXXAccessSpecifier.PRIVATE: + if node.access == cindex.AccessSpecifier.PRIVATE: return self.refid(node) diff --git a/cldoc/nodes/cclass.py b/cldoc/nodes/cclass.py index b89fda5..a5792f8 100644 --- a/cldoc/nodes/cclass.py +++ b/cldoc/nodes/cclass.py @@ -20,7 +20,7 @@ class Class(Node): kind = cindex.CursorKind.CLASS_DECL class Base: - def __init__(self, cursor, access=cindex.CXXAccessSpecifier.PUBLIC): + def __init__(self, cursor, access=cindex.AccessSpecifier.PUBLIC): self.cursor = cursor self.access = access self.type = Type(cursor.type, cursor=cursor) @@ -30,7 +30,7 @@ def __init__(self, cursor, comment): super(Class, self).__init__(cursor, comment) self.process_children = True - self.current_access = cindex.CXXAccessSpecifier.PRIVATE + self.current_access = cindex.AccessSpecifier.PRIVATE self.bases = [] self.implements = [] self.implemented_by = [] @@ -65,7 +65,7 @@ def resolve_nodes(self): yield child for base in self._all_bases(): - if base.node and base.access != cindex.CXXAccessSpecifier.PRIVATE: + if base.node and base.access != cindex.AccessSpecifier.PRIVATE: yield base.node for child in base.node.resolve_nodes: diff --git a/cldoc/nodes/cstruct.py b/cldoc/nodes/cstruct.py index e7630fc..e5d874d 100644 --- a/cldoc/nodes/cstruct.py +++ b/cldoc/nodes/cstruct.py @@ -21,7 +21,7 @@ def __init__(self, cursor, comment): Class.__init__(self, cursor, comment) self.typedef = None - self.current_access = cindex.CXXAccessSpecifier.PUBLIC + self.current_access = cindex.AccessSpecifier.PUBLIC @property def is_anonymous(self): diff --git a/cldoc/nodes/node.py b/cldoc/nodes/node.py index d0cf342..59bf1a3 100644 --- a/cldoc/nodes/node.py +++ b/cldoc/nodes/node.py @@ -38,14 +38,14 @@ def __init__(self, cursor, comment): self._comment = comment self.children = [] self.parent = None - self.access = cindex.CXXAccessSpecifier.PUBLIC + self.access = cindex.AccessSpecifier.PUBLIC self._comment_locations = [] self._refs = [] self.sort_index = 0 self.num_anon = 0 self.anonymous_id = 0 self._refid = None - + self.weight = 0 self.sortid = 0 cls = self.__class__ @@ -231,6 +231,20 @@ def semantic_parent(self): def title(self): return self.name + def output_filename(self,namespace_separator): + fn =self.qid.replace('::',namespace_separator) + elems =fn.split(':') + if len(elems)>1: + fn =elems[len(elems)-1] + else: + fn =elems[0] + if self.sort_index!=0: + parts=fn.split(namespace_separator) + if parts[len(parts)-1]!='index': + parts[len(parts)-1]=str(self.sort_index)+'_'+parts[len(parts)-1] + #fn=namespace_separator.join(parts) + return fn + @property def qid(self): meid = self.name @@ -263,14 +277,15 @@ def props(self): 'id': self.qid, 'name': self.name, 'title': self.title, + 'weight': self.weight, } if self.is_anonymous: ret['anonymous'] = 'yes' - if self.access == cindex.CXXAccessSpecifier.PROTECTED: + if self.access == cindex.AccessSpecifier.PROTECTED: ret['access'] = 'protected' - elif self.access == cindex.CXXAccessSpecifier.PRIVATE: + elif self.access == cindex.AccessSpecifier.PRIVATE: ret['access'] = 'private' return ret diff --git a/cldoc/nodes/templatetypeparameter.py b/cldoc/nodes/templatetypeparameter.py index 29d1271..f2115b5 100644 --- a/cldoc/nodes/templatetypeparameter.py +++ b/cldoc/nodes/templatetypeparameter.py @@ -38,7 +38,7 @@ def default_type(self): @property def access(self): - return cindex.CXXAccessSpecifier.PUBLIC + return cindex.AccessSpecifier.PUBLIC @access.setter def access(self, val): @@ -69,7 +69,7 @@ def name(self): @property def access(self): - return cindex.CXXAccessSpecifier.PUBLIC + return cindex.AccessSpecifier.PUBLIC @access.setter def access(self, val): diff --git a/cldoc/tree.py b/cldoc/tree.py index 7be47f6..b17c8d6 100644 --- a/cldoc/tree.py +++ b/cldoc/tree.py @@ -50,19 +50,32 @@ if not lname is None: cindex.Config.set_library_file(lname) else: - versions = [None, '3.5', '3.4', '3.3', '3.2'] + libclangs = [ + 'C:/Program Files (x86)/LLVM/bin' + ] + + found = False + + for libclang in libclangs: + fclang=libclang+'/libclang.dll' + if os.path.exists(fclang): + cindex.Config.set_library_path(os.path.dirname(fclang)) + found = True + break + if not found: + versions = [None, '3.5', '3.4', '3.3', '3.2'] - for v in versions: - name = 'clang' + for v in versions: + name = 'clang' - if not v is None: - name += '-' + v + if not v is None: + name += '-' + v - lname = find_library(name) + lname = find_library(name) - if not lname is None: - cindex.Config.set_library_file(lname) - break + if not lname is None: + cindex.Config.set_library_file(lname) + break testconf = cindex.Config() @@ -175,7 +188,11 @@ def process(self): fatal = False for d in tu.diagnostics: - sys.stderr.write(d.format) + #rewrite Clang errors in visual studio format. + # e.g. C:\\vector.h:249:43: error: function declared 'cdecl' here was previously declared without calling convention + # becomes C:\\vector.h(249): error: function declared 'cdecl' here was previously declared without calling convention + formatted=re.sub(":(\d+):(?:\d+):", "(\\1):", d.format) + sys.stderr.write(formatted) sys.stderr.write("\n") if d.severity == cindex.Diagnostic.Fatal or \ diff --git a/scripts/cldoc-dev b/scripts/cldoc-dev deleted file mode 100755 index 031a151..0000000 --- a/scripts/cldoc-dev +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/python - -import sys, os - -sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) - -os.environ["CLDOC_DEV"] = "1" - -import cldoc -cldoc.run() \ No newline at end of file From a622653a8e4eb72eb50c7a8ac875869777cc1af4 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Mon, 4 Sep 2017 11:20:58 +0100 Subject: [PATCH 09/32] Added --clean and --strip options. Preliminary support for a few doxygen-style commands like \image: mostly not functional yet. Added has_any_docs() to Node. --- cldoc.pyproj | 7 +- cldoc/cmdgenerate.py | 21 ++++- cldoc/comment.py | 65 +++++++++++---- cldoc/documentmerger.py | 2 +- cldoc/generators/md.py | 173 +++++++++++++++++++++++++++------------- cldoc/nodes/node.py | 11 +++ cldoc/tree.py | 8 +- 7 files changed, 208 insertions(+), 79 deletions(-) diff --git a/cldoc.pyproj b/cldoc.pyproj index 73892fd..fdafcfe 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -13,12 +13,15 @@ Standard Python launcher {2af0f10d-7135-4994-9156-5d01c9c11b7e} 2.7 - generate -IC:\\Simul\\4.2 -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\atlmfc\\include" -I"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --md_output ref C:/Simul/4.2/Simul/Base/*.h C:/Simul/4.2/Simul/Math/*.h --post "c:/Simul/4.2/Simul/Help/build.bat" + generate -IC:\\Simul\\4.2 -I"$(VCINSTALLDIR)\\atlmfc\\include" -I"$(VCINSTALLDIR)\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --strip "$(SIMUL)/" --md_output ref $(SIMUL)/Sky/*.h $(SIMUL)/Clouds/*.h --merge C:/Simul/4.2/Simul/Help --clean C:/Simul/docs/ref --post "$(SIMUL)/Help/build.bat" False Path=C:\Program Files\LLVM\bin;$(Path) -also=--merge C:/Simul/4.2/Simul/Help +also= +test= + + diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index 3ce562d..b19b36a 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -98,6 +98,12 @@ def run(args): parser.add_argument('--custom-css', default=[], metavar='FILES', action='append', help='specify additional css files to be merged into the html (only for when --output is html)') + parser.add_argument('--clean', default=None, metavar='CLEAN', + help='directory to clean before running') + + parser.add_argument('--strip', default=None, metavar='STRIP', + help='path to remove from filenames') + parser.add_argument('--post', default=None, metavar='POST', help='command to execute after completion') @@ -114,11 +120,22 @@ def run(args): opts.files=newfiles if opts.quiet: sys.stdout = open(os.devnull, 'w') + if opts.clean: + r = glob.glob(opts.clean+'/*') + for i in r: + if os.path.isdir(i): + shutil.rmtree(i) + else: + os.remove(i) log.setLevel(opts.loglevel) from . import tree - + + if opts.strip: + opts.strip=opts.strip.replace('\\','/') + opts.strip=opts.strip.replace('//','/') + if not opts.output: sys.stderr.write("Please specify the output directory\n") sys.exit(1) @@ -137,7 +154,7 @@ def run(args): cxxflags.append('-x') cxxflags.append(opts.language) - t = tree.Tree(opts.files, cxxflags) + t = tree.Tree(opts.files, cxxflags, opts) t.process() diff --git a/cldoc/comment.py b/cldoc/comment.py index bcd78e5..84db704 100644 --- a/cldoc/comment.py +++ b/cldoc/comment.py @@ -124,9 +124,9 @@ def __new__(cls, s): #redoccode = re.compile('^ \\[code\\]\n(?P(?:(?: .*|)\n)*)', re.M) redocmcode = re.compile('(^ *(`{3,}|~{3,}).*?\\2)', re.M | re.S) - re_dox_ref=re.compile('\\\\(?P[a-zA-Z]+)\\s+(?P\\w+)(?:\\s+"(?P\\w+)")?') + re_dox_ref=re.compile('\\\\(?P[a-zA-Z]+)\\s+(?P\\w+)(?:\\s+"(?P.*)")?') - def __init__(self, text, location): + def __init__(self, text, location, opts): self.__dict__['docstrings'] = [] self.__dict__['text'] = text @@ -135,6 +135,9 @@ def __init__(self, text, location): self.doc = text self.brief = '' + self.images=[] + self.imagepaths=[] + self.options=opts def __setattr__(self, name, val): if not name in self.docstrings: @@ -235,8 +238,15 @@ def resolve_refs_for_doc(self, doc, resolver, root): prefix, command, name, refname = pair components.append(prefix) if command==None: - pass - elif command=='ref': + continue + lineno=int(0) + filename='' + if self.location: + filename=self.location.file.name + if self.location.line: + lineno=self.location.line + + if command=='ref': if name is None: continue @@ -262,16 +272,34 @@ def resolve_refs_for_doc(self, doc, resolver, root): components.append((newnds, refname)) else: components.append(Comment.UnresolvedReference(name)) - elif command=='a': + elif command=='a' or command=='em': components.append('**'+name+'**') - elif command=='param': + elif command=='param' or command=='p': components.append('\n**'+name+'**') elif command=='return': components.append('\n**return:** '+name) elif command=='brief': pass + elif command=='image': + parts=os.path.split(filename); + path='' + if len(parts)>0: + path=parts[0] + imagepaths=[path] + found=False + for i in imagepaths: + filepath=i+'/'+refname + if os.path.exists(filepath): + found=True + break + if not found: + print(filename+' ('+str(lineno)+'): warning: Image not found: '+ filepath) + else: + components.append('[![alt text]("'+refname+' "'+refname+'")') + self.images.append(refname) + self.imagepaths.append(i) else: - print(self.location.file.name+' ('+str(self.location.line)+'): warning: Unknown command \\'+ command) + print(filename+' ('+str(lineno)+'): warning: Unknown command \\'+ command) doc.components = components @@ -337,12 +365,12 @@ def find(self, i): class CommentsDatabase(object): cldoc_instrre = re.compile('^cldoc:([a-zA-Z_-]+)(\(([^\)]*)\))?') - def __init__(self, filename, tu): + def __init__(self, filename, tu, opts): self.filename = filename self.categories = RangeMap() self.comments = Sorted(key=lambda x: x.location.offset) - + self.options=opts self.extract(filename, tu) def parse_cldoc_instruction(self, token, s): @@ -432,7 +460,7 @@ def extract_one(self, token, s): if self.parse_cldoc_instruction(token, s.strip()): return - comment = Comment(s, token.location) + comment = Comment(s, token.location,self.options) self.comments.insert(comment) def extract_loop(self, iter): @@ -514,21 +542,26 @@ def clean(self, token): class Parser: ParserElement.setDefaultWhitespaceChars(' \t\r') - + + #All variables defined on the class level in Python are considered static. + # Here, we define static members of the class Parser, from pyparsing: identifier = Word(alphas + '_', alphanums + '_') - - brief = restOfLine.setResultsName('brief') + lineEnd + + # I have modified the parser to make brief the default return, and body is optional. Only if there is a double lineEnd will there be a body, + # and it is everything from the first such double lineEnd to the end of the text. + briefline = NotAny('@') + ((Regex('[^\n]+') + lineEnd)) + brief = ZeroOrMore(lineEnd) + Combine(OneOrMore(briefline)).setResultsName('brief') paramdesc = restOfLine + ZeroOrMore(lineEnd + ~('@' | lineEnd) + Regex('[^\n]+')) + lineEnd.suppress() param = '@' + identifier.setResultsName('name') + White() + Combine(paramdesc).setResultsName('description') preparams = ZeroOrMore(param.setResultsName('preparam', listAllMatches=True)) postparams = ZeroOrMore(param.setResultsName('postparam', listAllMatches=True)) - + bodyline = NotAny('@') + (lineEnd | (Regex('[^\n]+') + lineEnd)) - body = ZeroOrMore(lineEnd) + Combine(ZeroOrMore(bodyline)).setResultsName('body') + body = OneOrMore(lineEnd) + Combine(ZeroOrMore(bodyline)).setResultsName('body') - doc = brief + preparams + body + postparams + doc = Optional(brief) + preparams + Optional(body) + postparams @staticmethod def parse(s): diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index 908709c..11aaa8c 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -139,7 +139,7 @@ def _merge_file(self, mfilter, filename): node = self.qid_to_node[qid] node.weight=weight if key == 'doc': - node.merge_comment(comment.Comment(docstr, None), override=True) + node.merge_comment(comment.Comment(docstr, None, self.options), override=True) else: sys.stderr.write('Unknown type `{0}\' for id `{1}\'\n'.format(key, parts[0])) sys.exit(1) diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index 9eca3d4..a28623c 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -136,14 +136,15 @@ def list_bases(self,f,elem): f.write('\n') def get_return_type(self,elem): - ret_type='' + ret_parts=[] for child in elem.getchildren(): if child.tag=='return': for c in child.getchildren(): if c.tag=='type': - ret_type+=c.attrib['name'] + ret_parts.append(c.attrib['name']) if c.attrib.has_key('qualifier'): - ret_type=c.attrib['qualifier']+' '; + ret_parts.append(c.attrib['qualifier']); + ret_type=' '.join(ret_parts); return ret_type def get_brief(self,elem): @@ -152,6 +153,17 @@ def get_brief(self,elem): if child.tag=='brief': brief=child.text return brief + + def get_location(self,elem): + location='' + if elem.attrib.has_key('location'): + return elem.attrib['location'] + return location + def get_lib(self,elem): + lib='' + if elem.attrib.has_key('lib'): + return elem.attrib['lib'] + return lib def get_doc(self,elem): doc='' @@ -206,6 +218,13 @@ def get_typed_arguments_text(self,elem): args_txt.append(tp) arglist=', '.join(args_txt) return arglist + + def has_doc(self,elem): + for child in elem.getchildren(): + if child.tag=='brief' or child.tag=='doc': + if child.text!='': + return True + return False def doc_method(self,f,elem): ret_type='' @@ -229,6 +248,15 @@ def doc_method(self,f,elem): if doc!='': f.write(doc+'\n') + def doc_field(self,f,elem): + doc='' + brief='' + for child in elem.getchildren(): + if child.tag=='brief': + brief=child.text + elif child.tag=='doc': + doc=child.text + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') def process_text(self,elem): res=elem.text @@ -248,8 +276,6 @@ def write_md(self, elem, fname): self.written[fname] = True - elem.attrib['xmlns'] = 'http://jessevdk.github.com/cldoc/1.0' - tree = ElementTree.ElementTree(elem) self.indent(tree.getroot()) @@ -279,6 +305,7 @@ def write_md(self, elem, fname): os.makedirs(self.current_path) except: pass + print('Documenting '+title) f = fs.fs.open(fullpath, 'w') f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\nweight: '+str(weight)+'\n---\n') @@ -294,7 +321,12 @@ def write_md(self, elem, fname): else: f.write(elem.tag+' '+title) f.write('\n===\n\n') - + location=self.get_location(elem) + lib=self.get_lib(elem) + if location and location!='': + f.write('| Include: | '+location+' |\n\n') + if lib and lib!='': + f.write('| Library: | '+lib+' |\n\n') brief=self.get_brief(elem) doc=self.get_doc(elem) @@ -307,42 +339,65 @@ def write_md(self, elem, fname): # method declarations f.write('\n') - any_methods=False - any_namespaces=False - any_classes=False - any_bases=False - - for child in elem.getchildren(): - if child.tag!='base': - continue - any_bases=True - + namespaces=[] + classes=[] + methods=[] + bases=[] + fields=[] + typedefs=[] + enums=[] + variables=[] for child in elem.getchildren(): - if child.tag!='method': - continue - f.write('\n| '+self.get_return_type(child)+' | ['+child.attrib['name']+'](#'+child.attrib['name']+')('+self.get_typed_arguments_text(child)+') |') - any_methods=True + if child.tag=='base': + bases.append(child) + elif child.tag=='class' or child.tag=='struct': + classes.append(child) + elif child.tag=='method' or child.tag=='function' or child.tag=='constructor' or child.tag=='destructor': + if self.has_doc(child): + methods.append(child) + elif child.tag=='namespace': + namespaces.append(child) + elif child.tag=='field': + fields.append(child) + elif child.tag=='typedef': + typedefs.append(child) + elif child.tag=='enum': + enums.append(child) + elif child.tag=='variable': + variables.append(child) + elif child.tag=='brief' or child.tag=='doc': + pass + else: + print(child.tag) - for child in elem.getchildren(): - if child.tag!='namespace': - continue + for child in namespaces: title=child.tag+' '+child.attrib['name'] ref=child.attrib['ref'] lnk=self.ref_to_link(ref) #f.write(self.link_md(title,ref)+'\n') br=self.get_brief(child) f.write('\n| ['+title+']('+lnk+') | '+br+' |') - any_namespaces=True - - for child in elem.getchildren(): - if child.tag!='class': - continue - title=child.tag+' '+child.attrib['name'] - ref=child.attrib['ref'] - lnk=self.ref_to_link(ref) - br=self.get_brief(child) - f.write('\n| ['+title+']('+lnk+') | '+br+' |') - any_classes=True + + if len(classes): + f.write('\nClasses and Structures\n---\n') + for child in classes: + title=child.attrib['name'] + ref=child.attrib['ref'] + if self.has_doc(child): + lnk=self.ref_to_link(ref) + title='['+title+']('+lnk+')' + + br=self.get_brief(child) + # can't have newlines without breaking the table structure. + br=br.replace('\n','
') + f.write('\n| '+child.tag+' '+title+' | '+br+' |') + f.write('\n') + + if len(methods): + f.write('\nFunctions\n---\n') + for child in methods: + f.write('\n| '+self.get_return_type(child)+' | ['+child.attrib['name']+'](#'+child.attrib['name']+')('+self.get_typed_arguments_text(child)+') |') + any_methods=True f.write('\n') # main text @@ -350,35 +405,35 @@ def write_md(self, elem, fname): f.write('\n'+doc+'\n') f.write('\n') - if any_bases: - f.write('Base Classes\n---\n') - for child in elem.getchildren(): + if len(bases)>0: + f.write('\nBase Classes\n---\n') + for child in bases: self.indent(child) if child.tag=='base': self.list_bases(f,child) - if any_methods: - f.write('Member Functions\n---\n') - for child in elem.getchildren(): + if len(methods)>0: + f.write('\nFunctions\n---\n') + for child in methods: self.indent(child) - if child.tag=='method': - self.doc_method(f,child) - + self.doc_method(f,child) + + if len(fields)>0: + f.write('\nFields\n---\n') + if len(variables)>0: + f.write('\nVariables\n---\n') + if len(typedefs)>0: + f.write('\nTypedefs\n---\n') + if len(enums)>0: + f.write('\nEnums\n---\n') + # children: for child in elem.getchildren(): self.indent(child) if child.tag=='base': self.list_bases(f,child) - elif child.tag=='namespace': - pass - elif child.tag=='constructor': - pass - elif child.tag=='class': - pass - elif child.tag=='method': - pass - else: - print child.tag + elif child.tag=='field': + self.doc_field(f,child) #tree.write(f, encoding='utf-8', xml_declaration=True) f.close() @@ -762,7 +817,14 @@ def node_to_md(self, node): for prop in props: if props[prop]: elem.set(prop, props[prop]) - + if node.cursor: + location=node.cursor.location.file.name + if location!='': + location=location.replace('\\','/') + location=location.replace('//','/') + if self.options.strip!=None: + location=location.replace(self.options.strip,'') + elem.set('location',location) if not node.comment is None and node.comment.brief: elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) @@ -794,6 +856,9 @@ def templated_to_md_ref(self, node, element): element.append(self.node_to_md(child)) def generate_page(self, node): + # ignore nodes containing no documentation + if not node.has_any_docs(): + return elem = self.node_to_md(node) self.namespace_separator='.' if self.namespaces_as_directories==True: diff --git a/cldoc/nodes/node.py b/cldoc/nodes/node.py index 59bf1a3..dc5da39 100644 --- a/cldoc/nodes/node.py +++ b/cldoc/nodes/node.py @@ -155,12 +155,23 @@ def comment_locations(self): for loc in self._comment_locations: yield loc + def has_any_docs(self): + if len(self.children)==0: + return self.comment!=None + else: + for i in self.children: + if i.has_any_docs(): + return True + return False + def parse_comment(self): # Just extract brief and doc + # RVK: Here, pyparsing is used. The grammar defined in Parser splits the comment into brief and (optionally) body. self._parsed_comment = Parser.parse(self._comment.text) if len(self._parsed_comment.brief) > 0: self._comment.brief = self._parsed_comment.brief + if len(self._parsed_comment.body) > 0: self._comment.doc = self._parsed_comment.body @property diff --git a/cldoc/tree.py b/cldoc/tree.py index b17c8d6..bfc3932 100644 --- a/cldoc/tree.py +++ b/cldoc/tree.py @@ -86,7 +86,7 @@ sys.exit(1) class Tree(documentmerger.DocumentMerger): - def __init__(self, files, flags): + def __init__(self, files, flags, options): self.processed = {} self.files, ok = self.expand_sources([os.path.realpath(f) for f in files]) @@ -120,6 +120,7 @@ def __init__(self, files, flags): # Map from category name to the nodes.Category for that category self.category_to_node = Defdict() + self.options=options # Map from filename to comment.CommentsDatabase self.commentsdbs = Defdict() @@ -221,7 +222,7 @@ def process(self): extractfiles.append(filename) for e in extractfiles: - db = comment.CommentsDatabase(e, tu) + db = comment.CommentsDatabase(e, tu, self.options) self.add_categories(db.category_names) self.commentsdbs[e] = db @@ -236,7 +237,6 @@ def process(self): # Construct hierarchy of nodes. for node in self.all_nodes: q = node.qid - if node.parent is None: par = self.find_parent(node) @@ -545,7 +545,7 @@ def visit(self, citer, parent=None): for node in ret: self.register_node(node, par) - ignoretop = [cindex.CursorKind.TYPE_REF, cindex.CursorKind.PARM_DECL] + ignoretop = [cindex.CursorKind.FRIEND, cindex.CursorKind.TYPE_REF, cindex.CursorKind.TEMPLATE_REF, cindex.CursorKind.NAMESPACE_REF,cindex.CursorKind.PARM_DECL] if (not par or ret is None) and not item.kind in ignoretop: log.warning("Unhandled cursor: %s", item.kind) From bae7a3fb738fcdf06ca75c8fdab6fa195bc45392 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Fri, 30 Mar 2018 20:51:59 +0100 Subject: [PATCH 10/32] Added cldoc-dev.py --- cldoc.pyproj | 8 +-- cldoc/clang/cindex.py | 31 +++++++-- cldoc/comment.py | 26 +++++--- cldoc/documentmerger.py | 14 ++-- cldoc/generators/generator.py | 3 +- cldoc/generators/md.py | 116 ++++++++++++++++++++++------------ cldoc/nodes/node.py | 4 +- cldoc/nodes/root.py | 8 +++ cldoc/tree.py | 15 +++-- scripts/cldoc-dev.py | 10 +++ 10 files changed, 165 insertions(+), 70 deletions(-) create mode 100644 scripts/cldoc-dev.py diff --git a/cldoc.pyproj b/cldoc.pyproj index fdafcfe..bf4574f 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -11,14 +11,14 @@ . {888888a0-9f3d-457c-b088-3a5042f75d52} Standard Python launcher - {2af0f10d-7135-4994-9156-5d01c9c11b7e} + {b8d00d34-b396-4360-907e-34b71f9b7c8d} 2.7 - generate -IC:\\Simul\\4.2 -I"$(VCINSTALLDIR)\\atlmfc\\include" -I"$(VCINSTALLDIR)\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --strip "$(SIMUL)/" --md_output ref $(SIMUL)/Sky/*.h $(SIMUL)/Clouds/*.h --merge C:/Simul/4.2/Simul/Help --clean C:/Simul/docs/ref --post "$(SIMUL)/Help/build.bat" + generate -IC:\\Simul\\4.2 -I"$(VCINSTALLDIR)\\atlmfc\\include" -I"$(VCINSTALLDIR)\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --strip "$(SIMUL)/" --md_output ref $(SIMUL)/Base/*.h $(SIMUL)/Math/*.h $(SIMUL)/Geometry/*.h $(SIMUL)/Sky/*.h $(SIMUL)/Clouds/*.h $(SIMUL)/Platform/CrossPlatform/*.h $(SIMUL)/Plugins/TrueSkyPluginRender/*.h --merge C:/Simul/4.2/Simul/Help --clean C:/Simul/docs/ref --post "$(SIMUL)/Help/build.bat" False Path=C:\Program Files\LLVM\bin;$(Path) -also= +also= test= @@ -125,7 +125,7 @@ test= - + diff --git a/cldoc/clang/cindex.py b/cldoc/clang/cindex.py index 8d1f668..b70c4ec 100644 --- a/cldoc/clang/cindex.py +++ b/cldoc/clang/cindex.py @@ -120,7 +120,6 @@ class TranslationUnitSaveError(Exception): def __init__(self, enumeration, message): assert isinstance(enumeration, int) - if enumeration < 1 or enumeration > 3: raise Exception("Encountered undefined TranslationUnit save error " "constant: %d. Please file a bug to have this " @@ -169,6 +168,12 @@ def from_result(res, fn, args): assert isinstance(res, _CXString) return conf.lib.clang_getCString(res) +class GenericLocation(): + def __init__(self,file,line,col): + self.file=file + self.line=line + self.column=col + class SourceLocation(Structure): """ A SourceLocation represents a particular location within a source file. @@ -195,7 +200,13 @@ def from_position(tu, file, line, column): a particular translation unit. """ return conf.lib.clang_getLocation(tu, file, line, column) - + @staticmethod + def from_file_line(file, line): + """ + Create a + """ + loc=GenericLocation(file,line,0) + return loc @staticmethod def from_offset(tu, file, offset): """Retrieve a SourceLocation from a given character offset. @@ -2444,6 +2455,7 @@ def from_source(cls, filename, args=None, unsaved_files=None, options=0, len(unsaved_files), options) if not ptr: + conf.unload_lib() raise TranslationUnitLoadError("Error parsing translation unit.") return cls(ptr, index=index) @@ -3680,6 +3692,7 @@ class Config: library_file = None compatibility_check = True loaded = False + _lib=None @staticmethod def set_library_path(path): @@ -3723,12 +3736,18 @@ def set_compatibility_check(check_status): Config.compatibility_check = check_status - @CachedProperty + def unload_lib(self): + _lib=None + + # was @CachedProperty but we need to reload in case of a crash. + @property def lib(self): - lib = self.get_cindex_library() - register_functions(lib, not Config.compatibility_check) + if self._lib: + return self._lib + self._lib = self.get_cindex_library() + register_functions(self._lib, not Config.compatibility_check) Config.loaded = True - return lib + return self._lib def get_filename(self): if Config.library_file: diff --git a/cldoc/comment.py b/cldoc/comment.py index 84db704..9566b1f 100644 --- a/cldoc/comment.py +++ b/cldoc/comment.py @@ -179,10 +179,11 @@ def redoc_split(self, doc): # First split examples components = self.redoccode_split(doc) - + line_offset=0 for c in components: if isinstance(c, Comment.Example) or isinstance(c, Comment.MarkdownCode): - ret.append((c, None, None, None)) + ret.append((line_offset, c, None, None, None)) + line_offset+=c.count('\n') else: lastpos = 0 pos=0 @@ -226,8 +227,9 @@ def redoc_split(self, doc): else: prefix=c[lastpos:] lastpos=len(c) - ret.append((prefix,command,ref, refname)) + ret.append((line_offset,prefix,command,ref, refname)) pos=lastpos + line_offset+=prefix.count('\n') return ret def resolve_refs_for_doc(self, doc, resolver, root): @@ -235,17 +237,21 @@ def resolve_refs_for_doc(self, doc, resolver, root): components = [] for pair in comps: - prefix, command, name, refname = pair + offset, prefix, command, name, refname = pair components.append(prefix) if command==None: continue lineno=int(0) filename='' if self.location: - filename=self.location.file.name - if self.location.line: - lineno=self.location.line - + if self.location.__class__.__name__=="SourceLocation": + filename=self.location.file.name + if self.location.line: + lineno=self.location.line + else: + filename=self.location + lineno=0 + lineno+=offset if command=='ref': if name is None: continue @@ -278,6 +284,8 @@ def resolve_refs_for_doc(self, doc, resolver, root): components.append('\n**'+name+'**') elif command=='return': components.append('\n**return:** '+name) + elif command=='toc': + components.append('\n###Contents\n') elif command=='brief': pass elif command=='image': @@ -299,6 +307,8 @@ def resolve_refs_for_doc(self, doc, resolver, root): self.images.append(refname) self.imagepaths.append(i) else: + if filename=='': + print(': warning: Unknown command \\'+ command) print(filename+' ('+str(lineno)+'): warning: Unknown command \\'+ command) doc.components = components diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index 11aaa8c..d60fc42 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -62,6 +62,8 @@ def _split_categories(self, filename, contents): this_weight=int(heading.group(2)) category=heading.group(3) this_title=heading.group(1).strip() + line=this_title + doc.append(line) else: doc.append(line) if not this_weight: @@ -81,8 +83,8 @@ def _split_categories(self, filename, contents): return [[c, ret[c],title[c],weight[c]] for c in ordered] def _normalized_qid(self, qid): - #if qid == 'index': - # return None + if qid == 'ref': + return None if qid.startswith('::'): return qid[2:] @@ -131,15 +133,19 @@ def _merge_file(self, mfilter, filename): #if len(parts) > 1: # key = parts[1] - + # 'ref' means the root of the reference: + if qid=='ref': + qid=None if not self.qid_to_node[qid]: self.add_categories([[qid,cat_title]]) node = self.category_to_node[qid] else: node = self.qid_to_node[qid] + if qid==None: + node.set_title(cat_title) node.weight=weight if key == 'doc': - node.merge_comment(comment.Comment(docstr, None, self.options), override=True) + node.merge_comment(comment.Comment(docstr, filename, self.options), override=True) else: sys.stderr.write('Unknown type `{0}\' for id `{1}\'\n'.format(key, parts[0])) sys.exit(1) diff --git a/cldoc/generators/generator.py b/cldoc/generators/generator.py index 4defc57..fd5a216 100644 --- a/cldoc/generators/generator.py +++ b/cldoc/generators/generator.py @@ -17,7 +17,8 @@ def __init__(self, tree=None, opts=None): def generate(self, outdir): self.outdir = outdir - + + self.generate_node(self.tree.root) for node in self.tree.root.sorted_children(): self.generate_node(node) diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index a28623c..0c8a3a8 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -152,6 +152,8 @@ def get_brief(self,elem): for child in elem.getchildren(): if child.tag=='brief': brief=child.text + # can't have newlines without breaking the table structure. + brief=brief.replace('\n','
') return brief def get_location(self,elem): @@ -169,7 +171,7 @@ def get_doc(self,elem): doc='' for child in elem.getchildren(): if child.tag=='doc': - doc=child.text + doc=self.process_elem(child) return doc def return_type(self,elem): @@ -224,6 +226,8 @@ def has_doc(self,elem): if child.tag=='brief' or child.tag=='doc': if child.text!='': return True + if self.has_doc(child): + return True return False def doc_method(self,f,elem): @@ -247,8 +251,7 @@ def doc_method(self,f,elem): f.write(brief+'\n') if doc!='': f.write(doc+'\n') - - def doc_field(self,f,elem): + def get_docs(self,elem): doc='' brief='' for child in elem.getchildren(): @@ -256,25 +259,45 @@ def doc_field(self,f,elem): brief=child.text elif child.tag=='doc': doc=child.text - f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + return brief,doc + + def doc_typedef(self,f,elem): + brief,doc=self.get_docs(elem) + if brief!='' or doc!='': + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + + def doc_enum(self,f,elem): + brief,doc=self.get_docs(elem) + if brief!='' or doc!='': + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + + def doc_field(self,f,elem): + brief,doc=self.get_docs(elem) + if brief!='' or doc!='': + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + + def process_text(self,txt): + return txt - def process_text(self,elem): + def process_elem(self,elem): res=elem.text for child in elem.getchildren(): if child.tag=="ref": title=child.text - link=self.ref_to_link(child.attrib['ref']) - #res+='[{0}]({1})'.format(title,link) - res+='
{0}'.format(title,link) + if child.attrib.has_key('ref'): + link=self.ref_to_link(child.attrib['ref']) + #res+='[{0}]({1})'.format(title,link) + res+='{0}'.format(title,link) res+=child.tail else: - res+=self.process_text(child) + res+=self.process_elem(child) res+=elem.tail return res def write_md(self, elem, fname): self.written[fname] = True + fullpath=os.path.join(self.outdir, fname) tree = ElementTree.ElementTree(elem) @@ -294,7 +317,6 @@ def write_md(self, elem, fname): layout_name='reference' # if(elem.tag=='category'): - fullpath=os.path.join(self.outdir, fname) #else: # fullpath=os.path.join(os.path.join(self.outdir, 'ref'),fname) @@ -305,30 +327,29 @@ def write_md(self, elem, fname): os.makedirs(self.current_path) except: pass - print('Documenting '+title) + location=self.get_location(elem) + print(location+' (0): Documenting '+title) f = fs.fs.open(fullpath, 'w') f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\nweight: '+str(weight)+'\n---\n') + brief=self.get_brief(elem) + doc=self.get_doc(elem) if(elem.tag=='category'): - f.write(title) - f.write('\n===\n') - for child in elem.getchildren(): - if child.tag=='doc': - f.write(self.process_text(child)) + #f.write(title) + #f.write('\n===\n\n') + f.write(brief) + f.write(doc) else: - if elem.tag=='index': - f.write('Reference') + if elem.tag=='index' or elem.tag=='root': + f.write(title) else: f.write(elem.tag+' '+title) f.write('\n===\n\n') - location=self.get_location(elem) lib=self.get_lib(elem) if location and location!='': f.write('| Include: | '+location+' |\n\n') if lib and lib!='': f.write('| Library: | '+lib+' |\n\n') - brief=self.get_brief(elem) - doc=self.get_doc(elem) if brief=='': brief=doc @@ -351,20 +372,26 @@ def write_md(self, elem, fname): if child.tag=='base': bases.append(child) elif child.tag=='class' or child.tag=='struct': - classes.append(child) + if self.has_doc(child): + classes.append(child) elif child.tag=='method' or child.tag=='function' or child.tag=='constructor' or child.tag=='destructor': if self.has_doc(child): methods.append(child) elif child.tag=='namespace': - namespaces.append(child) + if self.has_doc(child): + namespaces.append(child) elif child.tag=='field': - fields.append(child) + if self.has_doc(child): + fields.append(child) elif child.tag=='typedef': - typedefs.append(child) + if self.has_doc(child): + typedefs.append(child) elif child.tag=='enum': - enums.append(child) + if self.has_doc(child): + enums.append(child) elif child.tag=='variable': - variables.append(child) + if self.has_doc(child): + variables.append(child) elif child.tag=='brief' or child.tag=='doc': pass else: @@ -375,9 +402,15 @@ def write_md(self, elem, fname): ref=child.attrib['ref'] lnk=self.ref_to_link(ref) #f.write(self.link_md(title,ref)+'\n') - br=self.get_brief(child) + br=self.get_brief(child).replace('\n','') f.write('\n| ['+title+']('+lnk+') | '+br+' |') + # children: + if len(bases): + for child in bases: + self.indent(child) + self.list_bases(f,child) + if len(classes): f.write('\nClasses and Structures\n---\n') for child in classes: @@ -386,10 +419,9 @@ def write_md(self, elem, fname): if self.has_doc(child): lnk=self.ref_to_link(ref) title='['+title+']('+lnk+')' - + else: + continue br=self.get_brief(child) - # can't have newlines without breaking the table structure. - br=br.replace('\n','
') f.write('\n| '+child.tag+' '+title+' | '+br+' |') f.write('\n') @@ -424,16 +456,18 @@ def write_md(self, elem, fname): f.write('\nVariables\n---\n') if len(typedefs)>0: f.write('\nTypedefs\n---\n') + for child in typedefs: + self.indent(child) + self.doc_typedef(f,child) if len(enums)>0: - f.write('\nEnums\n---\n') + f.write('\nEnums\n---\n') + for child in enums: + self.indent(child) + self.doc_enum(f,child) - # children: - for child in elem.getchildren(): + for child in fields: self.indent(child) - if child.tag=='base': - self.list_bases(f,child) - elif child.tag=='field': - self.doc_field(f,child) + self.doc_field(f,child) #tree.write(f, encoding='utf-8', xml_declaration=True) f.close() @@ -771,6 +805,8 @@ def doc_to_md(self, parent, doc, tagname='doc'): if refname: last.text = refname + elif cc.title: + last.text=cc.title else: last.text = parent.qlbl_from(cc) @@ -895,8 +931,8 @@ def generate_node(self, node): if self.is_page(node): elem = self.node_to_md_ref(node) - - self.indexmap[node.parent].append(elem) + if node.parent: + self.indexmap[node.parent].append(elem) self.indexmap[node] = elem self.generate_page(node) diff --git a/cldoc/nodes/node.py b/cldoc/nodes/node.py index dc5da39..b999c4b 100644 --- a/cldoc/nodes/node.py +++ b/cldoc/nodes/node.py @@ -207,7 +207,7 @@ def resolve_nodes(self): @property def name(self): if self.cursor is None: - ret = '' + ret = 'ref' else: ret = self.cursor.spelling @@ -265,6 +265,8 @@ def qid(self): if not parent: if self.classname=='category': return meid + elif self.name=='ref': # root + return 'ref' else: return 'ref::'+meid else: diff --git a/cldoc/nodes/root.py b/cldoc/nodes/root.py index cc8f1aa..b045bdc 100644 --- a/cldoc/nodes/root.py +++ b/cldoc/nodes/root.py @@ -16,11 +16,19 @@ class Root(Node): def __init__(self): Node.__init__(self, None, None) + self._title='Root' @property def is_anonymous(self): return True + def set_title(self,t): + self._title=t + + @property + def title(self): + return self._title + def sorted_children(self): schildren = list(Node.sorted_children(self)) diff --git a/cldoc/tree.py b/cldoc/tree.py index bfc3932..e5642ac 100644 --- a/cldoc/tree.py +++ b/cldoc/tree.py @@ -51,7 +51,7 @@ cindex.Config.set_library_file(lname) else: libclangs = [ - 'C:/Program Files (x86)/LLVM/bin' + 'C:/Program Files/LLVM/bin' ] found = False @@ -182,8 +182,10 @@ def process(self): continue #print('Processing {0}'.format(os.path.basename(f))) - - tu = self.index.parse(f, self.flags) + try: + tu = self.index.parse(f, self.flags) + except: + continue if len(tu.diagnostics) != 0: fatal = False @@ -414,10 +416,11 @@ def find_parent(self, node): cursor = node.cursor # If node is a C function, then see if we should group it to a struct - parent = self.node_on_c_struct(node) + # RVK: Wait, what? + #parent = self.node_on_c_struct(node) - if parent: - return parent + #if parent: + # return parent while cursor: cursor = cursor.semantic_parent diff --git a/scripts/cldoc-dev.py b/scripts/cldoc-dev.py new file mode 100644 index 0000000..031a151 --- /dev/null +++ b/scripts/cldoc-dev.py @@ -0,0 +1,10 @@ +#!/usr/bin/python + +import sys, os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + +os.environ["CLDOC_DEV"] = "1" + +import cldoc +cldoc.run() \ No newline at end of file From f72099dcbe6c72b7fd576ad6da940093f5446960 Mon Sep 17 00:00:00 2001 From: Simul Build Date: Sun, 1 Apr 2018 16:19:55 +0100 Subject: [PATCH 11/32] spaces to tabs --- cldoc.pyproj | 7 +- cldoc/__init__.py | 70 +- cldoc/clang/__init__.py | 4 +- cldoc/clang/cindex.py | 4289 +++++++++++++------------- cldoc/clang/enumerations.py | 12 +- cldoc/cmdgenerate.py | 222 +- cldoc/cmdgir.py | 1360 ++++---- cldoc/cmdinspect.py | 36 +- cldoc/cmdserve.py | 106 +- cldoc/comment.py | 938 +++--- cldoc/defdict.py | 4 +- cldoc/documentmerger.py | 349 +-- cldoc/example.py | 10 +- cldoc/fs.py | 140 +- cldoc/generators/generator.py | 26 +- cldoc/generators/html.py | 82 +- cldoc/generators/md.py | 1671 +++++----- cldoc/generators/report.py | 302 +- cldoc/generators/search.py | 42 +- cldoc/generators/xml.py | 766 ++--- cldoc/includepaths.py | 58 +- cldoc/inspecttree.py | 160 +- cldoc/log.py | 18 +- cldoc/nodes/category.py | 48 +- cldoc/nodes/cclass.py | 160 +- cldoc/nodes/classtemplate.py | 44 +- cldoc/nodes/constructor.py | 6 +- cldoc/nodes/conversionfunction.py | 6 +- cldoc/nodes/cstruct.py | 48 +- cldoc/nodes/ctype.py | 360 +-- cldoc/nodes/destructor.py | 6 +- cldoc/nodes/enum.py | 66 +- cldoc/nodes/enumvalue.py | 30 +- cldoc/nodes/field.py | 12 +- cldoc/nodes/function.py | 166 +- cldoc/nodes/functiontemplate.py | 30 +- cldoc/nodes/method.py | 104 +- cldoc/nodes/namespace.py | 8 +- cldoc/nodes/node.py | 612 ++-- cldoc/nodes/root.py | 44 +- cldoc/nodes/templated.py | 58 +- cldoc/nodes/templatetypeparameter.py | 108 +- cldoc/nodes/typedef.py | 20 +- cldoc/nodes/union.py | 26 +- cldoc/nodes/variable.py | 8 +- cldoc/staticsite.py | 82 +- cldoc/struct.py | 34 +- cldoc/tree.py | 766 ++--- cldoc/utf8.py | 64 +- setup.py | 236 +- tests/regression.py | 104 +- 51 files changed, 6963 insertions(+), 6965 deletions(-) diff --git a/cldoc.pyproj b/cldoc.pyproj index bf4574f..d7fc9a0 100644 --- a/cldoc.pyproj +++ b/cldoc.pyproj @@ -11,9 +11,9 @@ . {888888a0-9f3d-457c-b088-3a5042f75d52} Standard Python launcher - {b8d00d34-b396-4360-907e-34b71f9b7c8d} + {9a7a9026-48c1-4688-9d5d-e5699d47d074} 2.7 - generate -IC:\\Simul\\4.2 -I"$(VCINSTALLDIR)\\atlmfc\\include" -I"$(VCINSTALLDIR)\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --strip "$(SIMUL)/" --md_output ref $(SIMUL)/Base/*.h $(SIMUL)/Math/*.h $(SIMUL)/Geometry/*.h $(SIMUL)/Sky/*.h $(SIMUL)/Clouds/*.h $(SIMUL)/Platform/CrossPlatform/*.h $(SIMUL)/Plugins/TrueSkyPluginRender/*.h --merge C:/Simul/4.2/Simul/Help --clean C:/Simul/docs/ref --post "$(SIMUL)/Help/build.bat" + generate -IC:\\Simul\\4.2 -I"$(VCINSTALLDIR)\\atlmfc\\include" -I"$(VCINSTALLDIR)\\include" -I"C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.10240.0\\ucrt" -I"C:\\Program Files\\LLVM\\bin\\..\\lib\\clang\\4.0.0\\include" -std=c++1y -fms-extensions -fms-compatibility -fms-compatibility-version=19 -Wno-deprecated-declarations -Wno-ignored-attributes -Wno-microsoft-template -D_MSC_VER=1900 -DDOXYGEN=1 -- --type=md --loglevel=info --output C:/Simul/docs --strip "$(SIMUL)/" --md_output ref $(SIMUL)/Base/*.h --merge C:/Simul/4.2/Simul/Help --clean C:/Simul/docs/ref --post "$(SIMUL)/Help/build.bat" False @@ -22,6 +22,7 @@ also= test= + False
@@ -125,6 +126,8 @@ test= + + diff --git a/cldoc/__init__.py b/cldoc/__init__.py index 69f6a85..1fc1f76 100644 --- a/cldoc/__init__.py +++ b/cldoc/__init__.py @@ -15,57 +15,57 @@ import sys def run_inspect(args): - from . import cmdinspect - cmdinspect.run(args) + from . import cmdinspect + cmdinspect.run(args) def run_serve(args): - from . import cmdserve - cmdserve.run(args) + from . import cmdserve + cmdserve.run(args) def run_generate(args): - from . import cmdgenerate - cmdgenerate.run(args) + from . import cmdgenerate + cmdgenerate.run(args) def run_gir(args): - from . import cmdgir - cmdgir.run(args) + from . import cmdgir + cmdgir.run(args) def print_available_commands(): - sys.stderr.write('Available commands:\n') + sys.stderr.write('Available commands:\n') - commands = ['inspect', 'serve', 'generate', 'gir'] + commands = ['inspect', 'serve', 'generate', 'gir'] - for c in commands: - sys.stderr.write(' ' + c + '\n') + for c in commands: + sys.stderr.write(' ' + c + '\n') - sys.stderr.write('\n') + sys.stderr.write('\n') def run(): - if len(sys.argv) <= 1: - sys.stderr.write('Please use: cldoc [command] [OPTIONS] [FILES...]\n\n') - print_available_commands() - sys.exit(1) + if len(sys.argv) <= 1: + sys.stderr.write('Please use: cldoc [command] [OPTIONS] [FILES...]\n\n') + print_available_commands() + sys.exit(1) - cmd = sys.argv[1] - rest = sys.argv[2:] + cmd = sys.argv[1] + rest = sys.argv[2:] - if cmd == 'inspect': - run_inspect(rest) - elif cmd == 'serve': - run_serve(rest) - elif cmd == 'generate': - run_generate(rest) - elif cmd == 'gir': - run_gir(rest) - elif cmd == '--help' or cmd == '-h': - sys.stderr.write('Please use: cldoc [command] --help\n\n') - print_available_commands() - sys.exit(1) - else: - sys.stderr.write('Unknown command `{0}\'\n'.format(cmd)) - sys.exit(1) + if cmd == 'inspect': + run_inspect(rest) + elif cmd == 'serve': + run_serve(rest) + elif cmd == 'generate': + run_generate(rest) + elif cmd == 'gir': + run_gir(rest) + elif cmd == '--help' or cmd == '-h': + sys.stderr.write('Please use: cldoc [command] --help\n\n') + print_available_commands() + sys.exit(1) + else: + sys.stderr.write('Unknown command `{0}\'\n'.format(cmd)) + sys.exit(1) if __name__ == '__main__': - run() + run() # vi:ts=4:et diff --git a/cldoc/clang/__init__.py b/cldoc/clang/__init__.py index 88f3081..2defb69 100644 --- a/cldoc/clang/__init__.py +++ b/cldoc/clang/__init__.py @@ -1,6 +1,6 @@ #===- __init__.py - Clang Python Bindings --------------------*- python -*--===# # -# The LLVM Compiler Infrastructure +# The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. @@ -17,7 +17,7 @@ cindex - Bindings for the Clang indexing library. + Bindings for the Clang indexing library. """ __all__ = ['cindex'] diff --git a/cldoc/clang/cindex.py b/cldoc/clang/cindex.py index b70c4ec..92a4273 100644 --- a/cldoc/clang/cindex.py +++ b/cldoc/clang/cindex.py @@ -12,7 +12,7 @@ # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. #===- cindex.py - Python Indexing Library Bindings -----------*- python -*--===# # -# The LLVM Compiler Infrastructure +# The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. @@ -38,20 +38,20 @@ Index - The top-level object which manages some global library state. + The top-level object which manages some global library state. TranslationUnit - High-level object encapsulating the AST for a single translation unit. These - can be loaded from .ast files or parsed on the fly. + High-level object encapsulating the AST for a single translation unit. These + can be loaded from .ast files or parsed on the fly. Cursor - Generic object for representing a node in the AST. + Generic object for representing a node in the AST. SourceRange, SourceLocation, and File - Objects representing information about the input source. + Objects representing information about the input source. Most object information is exposed using properties, when the underlying API call is efficient. @@ -90,557 +90,557 @@ ### Exception Classes ### class TranslationUnitLoadError(Exception): - """Represents an error that occurred when loading a TranslationUnit. + """Represents an error that occurred when loading a TranslationUnit. - This is raised in the case where a TranslationUnit could not be - instantiated due to failure in the libclang library. + This is raised in the case where a TranslationUnit could not be + instantiated due to failure in the libclang library. - FIXME: Make libclang expose additional error information in this scenario. - """ - pass + FIXME: Make libclang expose additional error information in this scenario. + """ + pass class TranslationUnitSaveError(Exception): - """Represents an error that occurred when saving a TranslationUnit. + """Represents an error that occurred when saving a TranslationUnit. - Each error has associated with it an enumerated value, accessible under - e.save_error. Consumers can compare the value with one of the ERROR_ - constants in this class. - """ + Each error has associated with it an enumerated value, accessible under + e.save_error. Consumers can compare the value with one of the ERROR_ + constants in this class. + """ - # Indicates that an unknown error occurred. This typically indicates that - # I/O failed during save. - ERROR_UNKNOWN = 1 + # Indicates that an unknown error occurred. This typically indicates that + # I/O failed during save. + ERROR_UNKNOWN = 1 - # Indicates that errors during translation prevented saving. The errors - # should be available via the TranslationUnit's diagnostics. - ERROR_TRANSLATION_ERRORS = 2 + # Indicates that errors during translation prevented saving. The errors + # should be available via the TranslationUnit's diagnostics. + ERROR_TRANSLATION_ERRORS = 2 - # Indicates that the translation unit was somehow invalid. - ERROR_INVALID_TU = 3 + # Indicates that the translation unit was somehow invalid. + ERROR_INVALID_TU = 3 - def __init__(self, enumeration, message): - assert isinstance(enumeration, int) - if enumeration < 1 or enumeration > 3: - raise Exception("Encountered undefined TranslationUnit save error " - "constant: %d. Please file a bug to have this " - "value supported." % enumeration) + def __init__(self, enumeration, message): + assert isinstance(enumeration, int) + if enumeration < 1 or enumeration > 3: + raise Exception("Encountered undefined TranslationUnit save error " + "constant: %d. Please file a bug to have this " + "value supported." % enumeration) - self.save_error = enumeration - Exception.__init__(self, 'Error %d: %s' % (enumeration, message)) + self.save_error = enumeration + Exception.__init__(self, 'Error %d: %s' % (enumeration, message)) ### Structures and Utility Classes ### class CachedProperty(object): - """Decorator that lazy-loads the value of a property. + """Decorator that lazy-loads the value of a property. - The first time the property is accessed, the original property function is - executed. The value it returns is set as the new value of that instance's - property, replacing the original method. - """ + The first time the property is accessed, the original property function is + executed. The value it returns is set as the new value of that instance's + property, replacing the original method. + """ - def __init__(self, wrapped): - self.wrapped = wrapped - try: - self.__doc__ = wrapped.__doc__ - except: - pass + def __init__(self, wrapped): + self.wrapped = wrapped + try: + self.__doc__ = wrapped.__doc__ + except: + pass - def __get__(self, instance, instance_type=None): - if instance is None: - return self + def __get__(self, instance, instance_type=None): + if instance is None: + return self - value = self.wrapped(instance) - setattr(instance, self.wrapped.__name__, value) + value = self.wrapped(instance) + setattr(instance, self.wrapped.__name__, value) - return value + return value class _CXString(Structure): - """Helper for transforming CXString results.""" + """Helper for transforming CXString results.""" - _fields_ = [("spelling", c_char_p), ("free", c_int)] + _fields_ = [("spelling", c_char_p), ("free", c_int)] - def __del__(self): - conf.lib.clang_disposeString(self) + def __del__(self): + conf.lib.clang_disposeString(self) - @staticmethod - def from_result(res, fn, args): - assert isinstance(res, _CXString) - return conf.lib.clang_getCString(res) + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, _CXString) + return conf.lib.clang_getCString(res) class GenericLocation(): - def __init__(self,file,line,col): - self.file=file - self.line=line - self.column=col + def __init__(self,file,line,col): + self.file=file + self.line=line + self.column=col class SourceLocation(Structure): - """ - A SourceLocation represents a particular location within a source file. - """ - _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] - _data = None - - def _get_instantiation(self): - if self._data is None: - f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() - conf.lib.clang_getInstantiationLocation(self, byref(f), byref(l), - byref(c), byref(o)) - if f: - f = File(f) - else: - f = None - self._data = (f, int(l.value), int(c.value), int(o.value)) - return self._data - - @staticmethod - def from_position(tu, file, line, column): - """ - Retrieve the source location associated with a given file/line/column in - a particular translation unit. - """ - return conf.lib.clang_getLocation(tu, file, line, column) - @staticmethod - def from_file_line(file, line): - """ - Create a - """ - loc=GenericLocation(file,line,0) - return loc - @staticmethod - def from_offset(tu, file, offset): - """Retrieve a SourceLocation from a given character offset. - - tu -- TranslationUnit file belongs to - file -- File instance to obtain offset from - offset -- Integer character offset within file - """ - return conf.lib.clang_getLocationForOffset(tu, file, offset) - - @property - def file(self): - """Get the file represented by this source location.""" - return self._get_instantiation()[0] - - @property - def line(self): - """Get the line represented by this source location.""" - return self._get_instantiation()[1] - - @property - def column(self): - """Get the column represented by this source location.""" - return self._get_instantiation()[2] - - @property - def offset(self): - """Get the file offset represented by this source location.""" - return self._get_instantiation()[3] - - def __eq__(self, other): - return conf.lib.clang_equalLocations(self, other) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - if self.file: - filename = self.file.name - else: - filename = None - return "" % ( - filename, self.line, self.column) + """ + A SourceLocation represents a particular location within a source file. + """ + _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] + _data = None + + def _get_instantiation(self): + if self._data is None: + f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() + conf.lib.clang_getInstantiationLocation(self, byref(f), byref(l), + byref(c), byref(o)) + if f: + f = File(f) + else: + f = None + self._data = (f, int(l.value), int(c.value), int(o.value)) + return self._data + + @staticmethod + def from_position(tu, file, line, column): + """ + Retrieve the source location associated with a given file/line/column in + a particular translation unit. + """ + return conf.lib.clang_getLocation(tu, file, line, column) + @staticmethod + def from_file_line(file, line): + """ + Create a + """ + loc=GenericLocation(file,line,0) + return loc + @staticmethod + def from_offset(tu, file, offset): + """Retrieve a SourceLocation from a given character offset. + + tu -- TranslationUnit file belongs to + file -- File instance to obtain offset from + offset -- Integer character offset within file + """ + return conf.lib.clang_getLocationForOffset(tu, file, offset) + + @property + def file(self): + """Get the file represented by this source location.""" + return self._get_instantiation()[0] + + @property + def line(self): + """Get the line represented by this source location.""" + return self._get_instantiation()[1] + + @property + def column(self): + """Get the column represented by this source location.""" + return self._get_instantiation()[2] + + @property + def offset(self): + """Get the file offset represented by this source location.""" + return self._get_instantiation()[3] + + def __eq__(self, other): + return conf.lib.clang_equalLocations(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + if self.file: + filename = self.file.name + else: + filename = None + return "" % ( + filename, self.line, self.column) class SourceRange(Structure): - """ - A SourceRange describes a range of source locations within the source - code. - """ - _fields_ = [ - ("ptr_data", c_void_p * 2), - ("begin_int_data", c_uint), - ("end_int_data", c_uint)] - - # FIXME: Eliminate this and make normal constructor? Requires hiding ctypes - # object. - @staticmethod - def from_locations(start, end): - return conf.lib.clang_getRange(start, end) - - @property - def start(self): - """ - Return a SourceLocation representing the first character within a - source range. - """ - return conf.lib.clang_getRangeStart(self) - - @property - def end(self): - """ - Return a SourceLocation representing the last character within a - source range. - """ - return conf.lib.clang_getRangeEnd(self) - - def __eq__(self, other): - return conf.lib.clang_equalRanges(self, other) - - def __ne__(self, other): - return not self.__eq__(other) - - def __contains__(self, other): - """Useful to detect the Token/Lexer bug""" - if not isinstance(other, SourceLocation): - return False - if other.file is None and self.start.file is None: - pass - elif ( self.start.file.name != other.file.name or - other.file.name != self.end.file.name): - # same file name - return False - # same file, in between lines - if self.start.line < other.line < self.end.line: - return True - elif self.start.line == other.line: - # same file first line - if self.start.column <= other.column: - return True - elif other.line == self.end.line: - # same file last line - if other.column <= self.end.column: - return True - return False - - def __repr__(self): - return "" % (self.start, self.end) + """ + A SourceRange describes a range of source locations within the source + code. + """ + _fields_ = [ + ("ptr_data", c_void_p * 2), + ("begin_int_data", c_uint), + ("end_int_data", c_uint)] + + # FIXME: Eliminate this and make normal constructor? Requires hiding ctypes + # object. + @staticmethod + def from_locations(start, end): + return conf.lib.clang_getRange(start, end) + + @property + def start(self): + """ + Return a SourceLocation representing the first character within a + source range. + """ + return conf.lib.clang_getRangeStart(self) + + @property + def end(self): + """ + Return a SourceLocation representing the last character within a + source range. + """ + return conf.lib.clang_getRangeEnd(self) + + def __eq__(self, other): + return conf.lib.clang_equalRanges(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def __contains__(self, other): + """Useful to detect the Token/Lexer bug""" + if not isinstance(other, SourceLocation): + return False + if other.file is None and self.start.file is None: + pass + elif ( self.start.file.name != other.file.name or + other.file.name != self.end.file.name): + # same file name + return False + # same file, in between lines + if self.start.line < other.line < self.end.line: + return True + elif self.start.line == other.line: + # same file first line + if self.start.column <= other.column: + return True + elif other.line == self.end.line: + # same file last line + if other.column <= self.end.column: + return True + return False + + def __repr__(self): + return "" % (self.start, self.end) class Diagnostic(object): - """ - A Diagnostic is a single instance of a Clang diagnostic. It includes the - diagnostic severity, the message, the location the diagnostic occurred, as - well as additional source ranges and associated fix-it hints. - """ - - Ignored = 0 - Note = 1 - Warning = 2 - Error = 3 - Fatal = 4 - - def __init__(self, ptr): - self.ptr = ptr - - def __del__(self): - conf.lib.clang_disposeDiagnostic(self) - - @property - def severity(self): - return conf.lib.clang_getDiagnosticSeverity(self) - - @property - def location(self): - return conf.lib.clang_getDiagnosticLocation(self) - - @property - def spelling(self): - return conf.lib.clang_getDiagnosticSpelling(self) - - @property - def ranges(self): - class RangeIterator: - def __init__(self, diag): - self.diag = diag - - def __len__(self): - return int(conf.lib.clang_getDiagnosticNumRanges(self.diag)) - - def __getitem__(self, key): - if (key >= len(self)): - raise IndexError - return conf.lib.clang_getDiagnosticRange(self.diag, key) - - return RangeIterator(self) - - @property - def fixits(self): - class FixItIterator: - def __init__(self, diag): - self.diag = diag - - def __len__(self): - return int(conf.lib.clang_getDiagnosticNumFixIts(self.diag)) - - def __getitem__(self, key): - range = SourceRange() - value = conf.lib.clang_getDiagnosticFixIt(self.diag, key, - byref(range)) - if len(value) == 0: - raise IndexError - - return FixIt(range, value) - - return FixItIterator(self) - - @property - def children(self): - class ChildDiagnosticsIterator: - def __init__(self, diag): - self.diag_set = conf.lib.clang_getChildDiagnostics(diag) - - def __len__(self): - return int(conf.lib.clang_getNumDiagnosticsInSet(self.diag_set)) - - def __getitem__(self, key): - diag = conf.lib.clang_getDiagnosticInSet(self.diag_set, key) - if not diag: - raise IndexError - return Diagnostic(diag) - - return ChildDiagnosticsIterator(self) - - @property - def category_number(self): - """The category number for this diagnostic or 0 if unavailable.""" - return conf.lib.clang_getDiagnosticCategory(self) - - @property - def category_name(self): - """The string name of the category for this diagnostic.""" - return conf.lib.clang_getDiagnosticCategoryText(self) - - @property - def option(self): - """The command-line option that enables this diagnostic.""" - return conf.lib.clang_getDiagnosticOption(self, None) - - @property - def format(self, options=-1): - if options == -1: - options = conf.lib.clang_defaultDiagnosticDisplayOptions() + """ + A Diagnostic is a single instance of a Clang diagnostic. It includes the + diagnostic severity, the message, the location the diagnostic occurred, as + well as additional source ranges and associated fix-it hints. + """ + + Ignored = 0 + Note = 1 + Warning = 2 + Error = 3 + Fatal = 4 + + def __init__(self, ptr): + self.ptr = ptr + + def __del__(self): + conf.lib.clang_disposeDiagnostic(self) + + @property + def severity(self): + return conf.lib.clang_getDiagnosticSeverity(self) + + @property + def location(self): + return conf.lib.clang_getDiagnosticLocation(self) + + @property + def spelling(self): + return conf.lib.clang_getDiagnosticSpelling(self) + + @property + def ranges(self): + class RangeIterator: + def __init__(self, diag): + self.diag = diag + + def __len__(self): + return int(conf.lib.clang_getDiagnosticNumRanges(self.diag)) + + def __getitem__(self, key): + if (key >= len(self)): + raise IndexError + return conf.lib.clang_getDiagnosticRange(self.diag, key) + + return RangeIterator(self) + + @property + def fixits(self): + class FixItIterator: + def __init__(self, diag): + self.diag = diag + + def __len__(self): + return int(conf.lib.clang_getDiagnosticNumFixIts(self.diag)) + + def __getitem__(self, key): + range = SourceRange() + value = conf.lib.clang_getDiagnosticFixIt(self.diag, key, + byref(range)) + if len(value) == 0: + raise IndexError + + return FixIt(range, value) + + return FixItIterator(self) + + @property + def children(self): + class ChildDiagnosticsIterator: + def __init__(self, diag): + self.diag_set = conf.lib.clang_getChildDiagnostics(diag) + + def __len__(self): + return int(conf.lib.clang_getNumDiagnosticsInSet(self.diag_set)) + + def __getitem__(self, key): + diag = conf.lib.clang_getDiagnosticInSet(self.diag_set, key) + if not diag: + raise IndexError + return Diagnostic(diag) + + return ChildDiagnosticsIterator(self) + + @property + def category_number(self): + """The category number for this diagnostic or 0 if unavailable.""" + return conf.lib.clang_getDiagnosticCategory(self) + + @property + def category_name(self): + """The string name of the category for this diagnostic.""" + return conf.lib.clang_getDiagnosticCategoryText(self) + + @property + def option(self): + """The command-line option that enables this diagnostic.""" + return conf.lib.clang_getDiagnosticOption(self, None) + + @property + def format(self, options=-1): + if options == -1: + options = conf.lib.clang_defaultDiagnosticDisplayOptions() - return conf.lib.clang_formatDiagnostic(self, options) + return conf.lib.clang_formatDiagnostic(self, options) - @property - def disable_option(self): - """The command-line option that disables this diagnostic.""" - disable = _CXString() - conf.lib.clang_getDiagnosticOption(self, byref(disable)) + @property + def disable_option(self): + """The command-line option that disables this diagnostic.""" + disable = _CXString() + conf.lib.clang_getDiagnosticOption(self, byref(disable)) - return conf.lib.clang_getCString(disable) + return conf.lib.clang_getCString(disable) - def __repr__(self): - return "" % ( - self.severity, self.location, self.spelling) + def __repr__(self): + return "" % ( + self.severity, self.location, self.spelling) - def from_param(self): - return self.ptr + def from_param(self): + return self.ptr class FixIt(object): - """ - A FixIt represents a transformation to be applied to the source to - "fix-it". The fix-it shouldbe applied by replacing the given source range - with the given value. - """ + """ + A FixIt represents a transformation to be applied to the source to + "fix-it". The fix-it shouldbe applied by replacing the given source range + with the given value. + """ - def __init__(self, range, value): - self.range = range - self.value = value + def __init__(self, range, value): + self.range = range + self.value = value - def __repr__(self): - return "" % (self.range, self.value) + def __repr__(self): + return "" % (self.range, self.value) class TokenGroup(object): - """Helper class to facilitate token management. + """Helper class to facilitate token management. - Tokens are allocated from libclang in chunks. They must be disposed of as a - collective group. + Tokens are allocated from libclang in chunks. They must be disposed of as a + collective group. - One purpose of this class is for instances to represent groups of allocated - tokens. Each token in a group contains a reference back to an instance of - this class. When all tokens from a group are garbage collected, it allows - this class to be garbage collected. When this class is garbage collected, - it calls the libclang destructor which invalidates all tokens in the group. + One purpose of this class is for instances to represent groups of allocated + tokens. Each token in a group contains a reference back to an instance of + this class. When all tokens from a group are garbage collected, it allows + this class to be garbage collected. When this class is garbage collected, + it calls the libclang destructor which invalidates all tokens in the group. - You should not instantiate this class outside of this module. - """ - def __init__(self, tu, memory, count): - self._tu = tu - self._memory = memory - self._count = count + You should not instantiate this class outside of this module. + """ + def __init__(self, tu, memory, count): + self._tu = tu + self._memory = memory + self._count = count - def __del__(self): - conf.lib.clang_disposeTokens(self._tu, self._memory, self._count) + def __del__(self): + conf.lib.clang_disposeTokens(self._tu, self._memory, self._count) - @staticmethod - def get_tokens(tu, extent): - """Helper method to return all tokens in an extent. + @staticmethod + def get_tokens(tu, extent): + """Helper method to return all tokens in an extent. - This functionality is needed multiple places in this module. We define - it here because it seems like a logical place. - """ - tokens_memory = POINTER(Token)() - tokens_count = c_uint() + This functionality is needed multiple places in this module. We define + it here because it seems like a logical place. + """ + tokens_memory = POINTER(Token)() + tokens_count = c_uint() - conf.lib.clang_tokenize(tu, extent, byref(tokens_memory), - byref(tokens_count)) + conf.lib.clang_tokenize(tu, extent, byref(tokens_memory), + byref(tokens_count)) - count = int(tokens_count.value) + count = int(tokens_count.value) - # If we get no tokens, no memory was allocated. Be sure not to return - # anything and potentially call a destructor on nothing. - if count < 1: - return + # If we get no tokens, no memory was allocated. Be sure not to return + # anything and potentially call a destructor on nothing. + if count < 1: + return - tokens_array = cast(tokens_memory, POINTER(Token * count)).contents + tokens_array = cast(tokens_memory, POINTER(Token * count)).contents - token_group = TokenGroup(tu, tokens_memory, tokens_count) + token_group = TokenGroup(tu, tokens_memory, tokens_count) - for i in xrange(0, count): - token = Token() - token.int_data = tokens_array[i].int_data - token.ptr_data = tokens_array[i].ptr_data - token._tu = tu - token._group = token_group + for i in xrange(0, count): + token = Token() + token.int_data = tokens_array[i].int_data + token.ptr_data = tokens_array[i].ptr_data + token._tu = tu + token._group = token_group - yield token + yield token class TokenKind(object): - """Describes a specific type of a Token.""" + """Describes a specific type of a Token.""" - _value_map = {} # int -> TokenKind + _value_map = {} # int -> TokenKind - def __init__(self, value, name): - """Create a new TokenKind instance from a numeric value and a name.""" - self.value = value - self.name = name + def __init__(self, value, name): + """Create a new TokenKind instance from a numeric value and a name.""" + self.value = value + self.name = name - def __repr__(self): - return 'TokenKind.%s' % (self.name,) + def __repr__(self): + return 'TokenKind.%s' % (self.name,) - @staticmethod - def from_value(value): - """Obtain a registered TokenKind instance from its value.""" - result = TokenKind._value_map.get(value, None) + @staticmethod + def from_value(value): + """Obtain a registered TokenKind instance from its value.""" + result = TokenKind._value_map.get(value, None) - if result is None: - raise ValueError('Unknown TokenKind: %d' % value) + if result is None: + raise ValueError('Unknown TokenKind: %d' % value) - return result + return result - @staticmethod - def register(value, name): - """Register a new TokenKind enumeration. + @staticmethod + def register(value, name): + """Register a new TokenKind enumeration. - This should only be called at module load time by code within this - package. - """ - if value in TokenKind._value_map: - raise ValueError('TokenKind already registered: %d' % value) + This should only be called at module load time by code within this + package. + """ + if value in TokenKind._value_map: + raise ValueError('TokenKind already registered: %d' % value) - kind = TokenKind(value, name) - TokenKind._value_map[value] = kind - setattr(TokenKind, name, kind) + kind = TokenKind(value, name) + TokenKind._value_map[value] = kind + setattr(TokenKind, name, kind) ### Cursor Kinds ### class BaseEnumeration(object): - """ - Common base class for named enumerations held in sync with Index.h values. - - Subclasses must define their own _kinds and _name_map members, as: - _kinds = [] - _name_map = None - These values hold the per-subclass instances and value-to-name mappings, - respectively. - - """ - - def __init__(self, value): - if value >= len(self.__class__._kinds): - self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) - if self.__class__._kinds[value] is not None: - raise ValueError,'{0} value {1} already loaded'.format( - str(self.__class__), value) - self.value = value - self.__class__._kinds[value] = self - self.__class__._name_map = None - - - def from_param(self): - return self.value - - @property - def name(self): - """Get the enumeration name of this cursor kind.""" - if self._name_map is None: - self._name_map = {} - for key, value in self.__class__.__dict__.items(): - if isinstance(value, self.__class__): - self._name_map[value] = key - return self._name_map[self] - - @classmethod - def from_id(cls, id): - if id >= len(cls._kinds) or cls._kinds[id] is None: - raise ValueError,'Unknown template argument kind %d' % id - return cls._kinds[id] - - def __repr__(self): - return '%s.%s' % (self.__class__, self.name,) + """ + Common base class for named enumerations held in sync with Index.h values. + + Subclasses must define their own _kinds and _name_map members, as: + _kinds = [] + _name_map = None + These values hold the per-subclass instances and value-to-name mappings, + respectively. + + """ + + def __init__(self, value): + if value >= len(self.__class__._kinds): + self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) + if self.__class__._kinds[value] is not None: + raise ValueError,'{0} value {1} already loaded'.format( + str(self.__class__), value) + self.value = value + self.__class__._kinds[value] = self + self.__class__._name_map = None + + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this cursor kind.""" + if self._name_map is None: + self._name_map = {} + for key, value in self.__class__.__dict__.items(): + if isinstance(value, self.__class__): + self._name_map[value] = key + return self._name_map[self] + + @classmethod + def from_id(cls, id): + if id >= len(cls._kinds) or cls._kinds[id] is None: + raise ValueError,'Unknown template argument kind %d' % id + return cls._kinds[id] + + def __repr__(self): + return '%s.%s' % (self.__class__, self.name,) class CursorKind(BaseEnumeration): - """ - A CursorKind describes the kind of entity that a cursor points to. - """ + """ + A CursorKind describes the kind of entity that a cursor points to. + """ - # The required BaseEnumeration declarations. - _kinds = [] - _name_map = None + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None - @staticmethod - def get_all_kinds(): - """Return all CursorKind enumeration instances.""" - return filter(None, CursorKind._kinds) + @staticmethod + def get_all_kinds(): + """Return all CursorKind enumeration instances.""" + return filter(None, CursorKind._kinds) - def is_declaration(self): - """Test if this is a declaration kind.""" - return conf.lib.clang_isDeclaration(self) + def is_declaration(self): + """Test if this is a declaration kind.""" + return conf.lib.clang_isDeclaration(self) - def is_reference(self): - """Test if this is a reference kind.""" - return conf.lib.clang_isReference(self) + def is_reference(self): + """Test if this is a reference kind.""" + return conf.lib.clang_isReference(self) - def is_expression(self): - """Test if this is an expression kind.""" - return conf.lib.clang_isExpression(self) + def is_expression(self): + """Test if this is an expression kind.""" + return conf.lib.clang_isExpression(self) - def is_statement(self): - """Test if this is a statement kind.""" - return conf.lib.clang_isStatement(self) + def is_statement(self): + """Test if this is a statement kind.""" + return conf.lib.clang_isStatement(self) - def is_attribute(self): - """Test if this is an attribute kind.""" - return conf.lib.clang_isAttribute(self) + def is_attribute(self): + """Test if this is an attribute kind.""" + return conf.lib.clang_isAttribute(self) - def is_invalid(self): - """Test if this is an invalid kind.""" - return conf.lib.clang_isInvalid(self) + def is_invalid(self): + """Test if this is an invalid kind.""" + return conf.lib.clang_isInvalid(self) - def is_translation_unit(self): - """Test if this is a translation unit kind.""" - return conf.lib.clang_isTranslationUnit(self) + def is_translation_unit(self): + """Test if this is a translation unit kind.""" + return conf.lib.clang_isTranslationUnit(self) - def is_preprocessing(self): - """Test if this is a preprocessing kind.""" - return conf.lib.clang_isPreprocessing(self) + def is_preprocessing(self): + """Test if this is a preprocessing kind.""" + return conf.lib.clang_isPreprocessing(self) - def is_unexposed(self): - """Test if this is an unexposed kind.""" - return conf.lib.clang_isUnexposed(self) + def is_unexposed(self): + """Test if this is an unexposed kind.""" + return conf.lib.clang_isUnexposed(self) - def __repr__(self): - return 'CursorKind.%s' % (self.name,) + def __repr__(self): + return 'CursorKind.%s' % (self.name,) ### # Declaration Kinds @@ -999,10 +999,10 @@ def __repr__(self): # # \code # void abssort(float *x, unsigned N) { -# std::sort(x, x + N, -# [](float a, float b) { -# return std::abs(a) < std::abs(b); -# }); +# std::sort(x, x + N, +# [](float a, float b) { +# return std::abs(a) < std::abs(b); +# }); # } # \endcode CursorKind.LAMBDA_EXPR = CursorKind(144) @@ -1176,14 +1176,14 @@ def __repr__(self): ### Template Argument Kinds ### class TemplateArgumentKind(BaseEnumeration): - """ - A TemplateArgumentKind describes the kind of entity that a template argument - represents. - """ + """ + A TemplateArgumentKind describes the kind of entity that a template argument + represents. + """ - # The required BaseEnumeration declarations. - _kinds = [] - _name_map = None + # The required BaseEnumeration declarations. + _kinds = [] + _name_map = None TemplateArgumentKind.NULL = TemplateArgumentKind(0) TemplateArgumentKind.TYPE = TemplateArgumentKind(1) @@ -1194,511 +1194,511 @@ class TemplateArgumentKind(BaseEnumeration): ### Cursors ### class Cursor(Structure): - """ - The Cursor class represents a reference to an element within the AST. It - acts as a kind of iterator. - """ - _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)] - - @staticmethod - def from_location(tu, location): - # We store a reference to the TU in the instance so the TU won't get - # collected before the cursor. - cursor = conf.lib.clang_getCursor(tu, location) - cursor._tu = tu - - return cursor - - def __eq__(self, other): - return conf.lib.clang_equalCursors(self, other) - - def __ne__(self, other): - return not self.__eq__(other) - - def is_definition(self): - """ - Returns true if the declaration pointed at by the cursor is also a - definition of that entity. - """ - return conf.lib.clang_isCursorDefinition(self) - - def is_const_method(self): - """Returns True if the cursor refers to a C++ member function or member - function template that is declared 'const'. - """ - return conf.lib.clang_CXXMethod_isConst(self) - - def is_converting_constructor(self): - """Returns True if the cursor refers to a C++ converting constructor. - """ - return conf.lib.clang_CXXConstructor_isConvertingConstructor(self) - - def is_copy_constructor(self): - """Returns True if the cursor refers to a C++ copy constructor. - """ - return conf.lib.clang_CXXConstructor_isCopyConstructor(self) - - def is_default_constructor(self): - """Returns True if the cursor refers to a C++ default constructor. - """ - return conf.lib.clang_CXXConstructor_isDefaultConstructor(self) - - def is_move_constructor(self): - """Returns True if the cursor refers to a C++ move constructor. - """ - return conf.lib.clang_CXXConstructor_isMoveConstructor(self) - - def is_default_method(self): - """Returns True if the cursor refers to a C++ member function or member - function template that is declared '= default'. - """ - return conf.lib.clang_CXXMethod_isDefaulted(self) - - def is_mutable_field(self): - """Returns True if the cursor refers to a C++ field that is declared - 'mutable'. - """ - return conf.lib.clang_CXXField_isMutable(self) - - def is_pure_virtual_method(self): - """Returns True if the cursor refers to a C++ member function or member - function template that is declared pure virtual. - """ - return conf.lib.clang_CXXMethod_isPureVirtual(self) - - def is_static_method(self): - """Returns True if the cursor refers to a C++ member function or member - function template that is declared 'static'. - """ - return conf.lib.clang_CXXMethod_isStatic(self) - - def is_virtual_method(self): - """Returns True if the cursor refers to a C++ member function or member - function template that is declared 'virtual'. - """ - return conf.lib.clang_CXXMethod_isVirtual(self) - - def get_definition(self): - """ - If the cursor is a reference to a declaration or a declaration of - some entity, return a cursor that points to the definition of that - entity. - """ - # TODO: Should probably check that this is either a reference or - # declaration prior to issuing the lookup. - return conf.lib.clang_getCursorDefinition(self) - - def get_usr(self): - """Return the Unified Symbol Resultion (USR) for the entity referenced - by the given cursor (or None). - - A Unified Symbol Resolution (USR) is a string that identifies a - particular entity (function, class, variable, etc.) within a - program. USRs can be compared across translation units to determine, - e.g., when references in one translation refer to an entity defined in - another translation unit.""" - return conf.lib.clang_getCursorUSR(self) - - @property - def kind(self): - """Return the kind of this cursor.""" - return CursorKind.from_id(self._kind_id) - - @property - def spelling(self): - """Return the spelling of the entity pointed at by the cursor.""" - if not hasattr(self, '_spelling'): - self._spelling = conf.lib.clang_getCursorSpelling(self) - - return self._spelling - - @property - def displayname(self): - """ - Return the display name for the entity referenced by this cursor. - - The display name contains extra information that helps identify the - cursor, such as the parameters of a function or template or the - arguments of a class template specialization. - """ - if not hasattr(self, '_displayname'): - self._displayname = conf.lib.clang_getCursorDisplayName(self) - - return self._displayname - - @property - def mangled_name(self): - """Return the mangled name for the entity referenced by this cursor.""" - if not hasattr(self, '_mangled_name'): - self._mangled_name = conf.lib.clang_Cursor_getMangling(self) - - return self._mangled_name - - @property - def location(self): - """ - Return the source location (the starting character) of the entity - pointed at by the cursor. - """ - if not hasattr(self, '_loc'): - self._loc = conf.lib.clang_getCursorLocation(self) - - return self._loc - - @property - def extent(self): - """ - Return the source range (the range of text) occupied by the entity - pointed at by the cursor. - """ - if not hasattr(self, '_extent'): - self._extent = conf.lib.clang_getCursorExtent(self) - - return self._extent - - @property - def storage_class(self): - """ - Retrieves the storage class (if any) of the entity pointed at by the - cursor. - """ - if not hasattr(self, '_storage_class'): - self._storage_class = conf.lib.clang_Cursor_getStorageClass(self) - - return StorageClass.from_id(self._storage_class) - - @property - def access_specifier(self): - """ - Retrieves the access specifier (if any) of the entity pointed at by the - cursor. - """ - if not hasattr(self, '_access_specifier'): - self._access_specifier = conf.lib.clang_getCXXAccessSpecifier(self) - - return AccessSpecifier.from_id(self._access_specifier) - - @property - def type(self): - """ - Retrieve the Type (if any) of the entity pointed at by the cursor. - """ - if not hasattr(self, '_type'): - self._type = conf.lib.clang_getCursorType(self) - - return self._type - - @property - def canonical(self): - """Return the canonical Cursor corresponding to this Cursor. - - The canonical cursor is the cursor which is representative for the - underlying entity. For example, if you have multiple forward - declarations for the same class, the canonical cursor for the forward - declarations will be identical. - """ - if not hasattr(self, '_canonical'): - self._canonical = conf.lib.clang_getCanonicalCursor(self) - - return self._canonical - - @property - def result_type(self): - """Retrieve the Type of the result for this Cursor.""" - if not hasattr(self, '_result_type'): - self._result_type = conf.lib.clang_getResultType(self.type) - - return self._result_type - - @property - def underlying_typedef_type(self): - """Return the underlying type of a typedef declaration. - - Returns a Type for the typedef this cursor is a declaration for. If - the current cursor is not a typedef, this raises. - """ - if not hasattr(self, '_underlying_type'): - assert self.kind.is_declaration() - self._underlying_type = \ - conf.lib.clang_getTypedefDeclUnderlyingType(self) - - return self._underlying_type - - @property - def enum_type(self): - """Return the integer type of an enum declaration. - - Returns a Type corresponding to an integer. If the cursor is not for an - enum, this raises. - """ - if not hasattr(self, '_enum_type'): - assert self.kind == CursorKind.ENUM_DECL - self._enum_type = conf.lib.clang_getEnumDeclIntegerType(self) - - return self._enum_type - - @property - def enum_value(self): - """Return the value of an enum constant.""" - if not hasattr(self, '_enum_value'): - assert self.kind == CursorKind.ENUM_CONSTANT_DECL - # Figure out the underlying type of the enum to know if it - # is a signed or unsigned quantity. - underlying_type = self.type - if underlying_type.kind == TypeKind.ENUM: - underlying_type = underlying_type.get_declaration().enum_type - if underlying_type.kind in (TypeKind.CHAR_U, - TypeKind.UCHAR, - TypeKind.CHAR16, - TypeKind.CHAR32, - TypeKind.USHORT, - TypeKind.UINT, - TypeKind.ULONG, - TypeKind.ULONGLONG, - TypeKind.UINT128): - self._enum_value = \ - conf.lib.clang_getEnumConstantDeclUnsignedValue(self) - else: - self._enum_value = conf.lib.clang_getEnumConstantDeclValue(self) - return self._enum_value - - @property - def objc_type_encoding(self): - """Return the Objective-C type encoding as a str.""" - if not hasattr(self, '_objc_type_encoding'): - self._objc_type_encoding = \ - conf.lib.clang_getDeclObjCTypeEncoding(self) - - return self._objc_type_encoding - - @property - def hash(self): - """Returns a hash of the cursor as an int.""" - if not hasattr(self, '_hash'): - self._hash = conf.lib.clang_hashCursor(self) - - return self._hash - - def __hash__(self): - return self.hash - - @property - def semantic_parent(self): - """Return the semantic parent for this cursor.""" - if not hasattr(self, '_semantic_parent'): - self._semantic_parent = conf.lib.clang_getCursorSemanticParent(self) - - return self._semantic_parent - - @property - def lexical_parent(self): - """Return the lexical parent for this cursor.""" - if not hasattr(self, '_lexical_parent'): - self._lexical_parent = conf.lib.clang_getCursorLexicalParent(self) - - return self._lexical_parent - - @property - def translation_unit(self): - """Returns the TranslationUnit to which this Cursor belongs.""" - # If this triggers an AttributeError, the instance was not properly - # created. - return self._tu - - @property - def referenced(self): - """ - For a cursor that is a reference, returns a cursor - representing the entity that it references. - """ - if not hasattr(self, '_referenced'): - self._referenced = conf.lib.clang_getCursorReferenced(self) - - return self._referenced - - @property - def brief_comment(self): - """Returns the brief comment text associated with that Cursor""" - return conf.lib.clang_Cursor_getBriefCommentText(self) - - @property - def raw_comment(self): - """Returns the raw comment text associated with that Cursor""" - return conf.lib.clang_Cursor_getRawCommentText(self) - - def get_arguments(self): - """Return an iterator for accessing the arguments of this cursor.""" - num_args = conf.lib.clang_Cursor_getNumArguments(self) - for i in range(0, num_args): - yield conf.lib.clang_Cursor_getArgument(self, i) - - def get_num_template_arguments(self): - """Returns the number of template args associated with this cursor.""" - return conf.lib.clang_Cursor_getNumTemplateArguments(self) - - def get_template_argument_kind(self, num): - """Returns the TemplateArgumentKind for the indicated template - argument.""" - return conf.lib.clang_Cursor_getTemplateArgumentKind(self, num) - - def get_template_argument_type(self, num): - """Returns the CXType for the indicated template argument.""" - return conf.lib.clang_Cursor_getTemplateArgumentType(self, num) - - def get_template_argument_value(self, num): - """Returns the value of the indicated arg as a signed 64b integer.""" - return conf.lib.clang_Cursor_getTemplateArgumentValue(self, num) - - def get_template_argument_unsigned_value(self, num): - """Returns the value of the indicated arg as an unsigned 64b integer.""" - return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num) - - def get_children(self): - """Return an iterator for accessing the children of this cursor.""" - - # FIXME: Expose iteration from CIndex, PR6125. - def visitor(child, parent, children): - # FIXME: Document this assertion in API. - # FIXME: There should just be an isNull method. - assert child != conf.lib.clang_getNullCursor() - - # Create reference to TU so it isn't GC'd before Cursor. - child._tu = self._tu - children.append(child) - return 1 # continue - children = [] - conf.lib.clang_visitChildren(self, callbacks['cursor_visit'](visitor), - children) - return iter(children) - - def walk_preorder(self): - """Depth-first preorder walk over the cursor and its descendants. - - Yields cursors. - """ - yield self - for child in self.get_children(): - for descendant in child.walk_preorder(): - yield descendant - - def get_tokens(self): - """Obtain Token instances formulating that compose this Cursor. - - This is a generator for Token instances. It returns all tokens which - occupy the extent this cursor occupies. - """ - return TokenGroup.get_tokens(self._tu, self.extent) - - def get_field_offsetof(self): - """Returns the offsetof the FIELD_DECL pointed by this Cursor.""" - return conf.lib.clang_Cursor_getOffsetOfField(self) - - def is_anonymous(self): - """ - Check if the record is anonymous. - """ - if self.kind == CursorKind.FIELD_DECL: - return self.type.get_declaration().is_anonymous() - return conf.lib.clang_Cursor_isAnonymous(self) - - def is_bitfield(self): - """ - Check if the field is a bitfield. - """ - return conf.lib.clang_Cursor_isBitField(self) - - def get_bitfield_width(self): - """ - Retrieve the width of a bitfield. - """ - return conf.lib.clang_getFieldDeclBitWidth(self) - - @property - def specialized_cursor_template(self): - """ - Retrieve the specialized cursor template. - """ - return conf.lib.clang_getSpecializedCursorTemplate(self) - - @property - def template_cursor_kind(self): - """ - Retrieve the template cursor kind. - """ - return conf.lib.clang_getTemplateCursorKind(self) - - @staticmethod - def from_result(res, fn, args): - assert isinstance(res, Cursor) - # FIXME: There should just be an isNull method. - if res == conf.lib.clang_getNullCursor(): - return None - - # Store a reference to the TU in the Python object so it won't get GC'd - # before the Cursor. - tu = None - for arg in args: - if isinstance(arg, TranslationUnit): - tu = arg - break - - if hasattr(arg, 'translation_unit'): - tu = arg.translation_unit - break - - assert tu is not None - - res._tu = tu - return res - - @staticmethod - def from_cursor_result(res, fn, args): - assert isinstance(res, Cursor) - if res == conf.lib.clang_getNullCursor(): - return None - - res._tu = args[0]._tu - return res + """ + The Cursor class represents a reference to an element within the AST. It + acts as a kind of iterator. + """ + _fields_ = [("_kind_id", c_int), ("xdata", c_int), ("data", c_void_p * 3)] + + @staticmethod + def from_location(tu, location): + # We store a reference to the TU in the instance so the TU won't get + # collected before the cursor. + cursor = conf.lib.clang_getCursor(tu, location) + cursor._tu = tu + + return cursor + + def __eq__(self, other): + return conf.lib.clang_equalCursors(self, other) + + def __ne__(self, other): + return not self.__eq__(other) + + def is_definition(self): + """ + Returns true if the declaration pointed at by the cursor is also a + definition of that entity. + """ + return conf.lib.clang_isCursorDefinition(self) + + def is_const_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'const'. + """ + return conf.lib.clang_CXXMethod_isConst(self) + + def is_converting_constructor(self): + """Returns True if the cursor refers to a C++ converting constructor. + """ + return conf.lib.clang_CXXConstructor_isConvertingConstructor(self) + + def is_copy_constructor(self): + """Returns True if the cursor refers to a C++ copy constructor. + """ + return conf.lib.clang_CXXConstructor_isCopyConstructor(self) + + def is_default_constructor(self): + """Returns True if the cursor refers to a C++ default constructor. + """ + return conf.lib.clang_CXXConstructor_isDefaultConstructor(self) + + def is_move_constructor(self): + """Returns True if the cursor refers to a C++ move constructor. + """ + return conf.lib.clang_CXXConstructor_isMoveConstructor(self) + + def is_default_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared '= default'. + """ + return conf.lib.clang_CXXMethod_isDefaulted(self) + + def is_mutable_field(self): + """Returns True if the cursor refers to a C++ field that is declared + 'mutable'. + """ + return conf.lib.clang_CXXField_isMutable(self) + + def is_pure_virtual_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared pure virtual. + """ + return conf.lib.clang_CXXMethod_isPureVirtual(self) + + def is_static_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'static'. + """ + return conf.lib.clang_CXXMethod_isStatic(self) + + def is_virtual_method(self): + """Returns True if the cursor refers to a C++ member function or member + function template that is declared 'virtual'. + """ + return conf.lib.clang_CXXMethod_isVirtual(self) + + def get_definition(self): + """ + If the cursor is a reference to a declaration or a declaration of + some entity, return a cursor that points to the definition of that + entity. + """ + # TODO: Should probably check that this is either a reference or + # declaration prior to issuing the lookup. + return conf.lib.clang_getCursorDefinition(self) + + def get_usr(self): + """Return the Unified Symbol Resultion (USR) for the entity referenced + by the given cursor (or None). + + A Unified Symbol Resolution (USR) is a string that identifies a + particular entity (function, class, variable, etc.) within a + program. USRs can be compared across translation units to determine, + e.g., when references in one translation refer to an entity defined in + another translation unit.""" + return conf.lib.clang_getCursorUSR(self) + + @property + def kind(self): + """Return the kind of this cursor.""" + return CursorKind.from_id(self._kind_id) + + @property + def spelling(self): + """Return the spelling of the entity pointed at by the cursor.""" + if not hasattr(self, '_spelling'): + self._spelling = conf.lib.clang_getCursorSpelling(self) + + return self._spelling + + @property + def displayname(self): + """ + Return the display name for the entity referenced by this cursor. + + The display name contains extra information that helps identify the + cursor, such as the parameters of a function or template or the + arguments of a class template specialization. + """ + if not hasattr(self, '_displayname'): + self._displayname = conf.lib.clang_getCursorDisplayName(self) + + return self._displayname + + @property + def mangled_name(self): + """Return the mangled name for the entity referenced by this cursor.""" + if not hasattr(self, '_mangled_name'): + self._mangled_name = conf.lib.clang_Cursor_getMangling(self) + + return self._mangled_name + + @property + def location(self): + """ + Return the source location (the starting character) of the entity + pointed at by the cursor. + """ + if not hasattr(self, '_loc'): + self._loc = conf.lib.clang_getCursorLocation(self) + + return self._loc + + @property + def extent(self): + """ + Return the source range (the range of text) occupied by the entity + pointed at by the cursor. + """ + if not hasattr(self, '_extent'): + self._extent = conf.lib.clang_getCursorExtent(self) + + return self._extent + + @property + def storage_class(self): + """ + Retrieves the storage class (if any) of the entity pointed at by the + cursor. + """ + if not hasattr(self, '_storage_class'): + self._storage_class = conf.lib.clang_Cursor_getStorageClass(self) + + return StorageClass.from_id(self._storage_class) + + @property + def access_specifier(self): + """ + Retrieves the access specifier (if any) of the entity pointed at by the + cursor. + """ + if not hasattr(self, '_access_specifier'): + self._access_specifier = conf.lib.clang_getCXXAccessSpecifier(self) + + return AccessSpecifier.from_id(self._access_specifier) + + @property + def type(self): + """ + Retrieve the Type (if any) of the entity pointed at by the cursor. + """ + if not hasattr(self, '_type'): + self._type = conf.lib.clang_getCursorType(self) + + return self._type + + @property + def canonical(self): + """Return the canonical Cursor corresponding to this Cursor. + + The canonical cursor is the cursor which is representative for the + underlying entity. For example, if you have multiple forward + declarations for the same class, the canonical cursor for the forward + declarations will be identical. + """ + if not hasattr(self, '_canonical'): + self._canonical = conf.lib.clang_getCanonicalCursor(self) + + return self._canonical + + @property + def result_type(self): + """Retrieve the Type of the result for this Cursor.""" + if not hasattr(self, '_result_type'): + self._result_type = conf.lib.clang_getResultType(self.type) + + return self._result_type + + @property + def underlying_typedef_type(self): + """Return the underlying type of a typedef declaration. + + Returns a Type for the typedef this cursor is a declaration for. If + the current cursor is not a typedef, this raises. + """ + if not hasattr(self, '_underlying_type'): + assert self.kind.is_declaration() + self._underlying_type = \ + conf.lib.clang_getTypedefDeclUnderlyingType(self) + + return self._underlying_type + + @property + def enum_type(self): + """Return the integer type of an enum declaration. + + Returns a Type corresponding to an integer. If the cursor is not for an + enum, this raises. + """ + if not hasattr(self, '_enum_type'): + assert self.kind == CursorKind.ENUM_DECL + self._enum_type = conf.lib.clang_getEnumDeclIntegerType(self) + + return self._enum_type + + @property + def enum_value(self): + """Return the value of an enum constant.""" + if not hasattr(self, '_enum_value'): + assert self.kind == CursorKind.ENUM_CONSTANT_DECL + # Figure out the underlying type of the enum to know if it + # is a signed or unsigned quantity. + underlying_type = self.type + if underlying_type.kind == TypeKind.ENUM: + underlying_type = underlying_type.get_declaration().enum_type + if underlying_type.kind in (TypeKind.CHAR_U, + TypeKind.UCHAR, + TypeKind.CHAR16, + TypeKind.CHAR32, + TypeKind.USHORT, + TypeKind.UINT, + TypeKind.ULONG, + TypeKind.ULONGLONG, + TypeKind.UINT128): + self._enum_value = \ + conf.lib.clang_getEnumConstantDeclUnsignedValue(self) + else: + self._enum_value = conf.lib.clang_getEnumConstantDeclValue(self) + return self._enum_value + + @property + def objc_type_encoding(self): + """Return the Objective-C type encoding as a str.""" + if not hasattr(self, '_objc_type_encoding'): + self._objc_type_encoding = \ + conf.lib.clang_getDeclObjCTypeEncoding(self) + + return self._objc_type_encoding + + @property + def hash(self): + """Returns a hash of the cursor as an int.""" + if not hasattr(self, '_hash'): + self._hash = conf.lib.clang_hashCursor(self) + + return self._hash + + def __hash__(self): + return self.hash + + @property + def semantic_parent(self): + """Return the semantic parent for this cursor.""" + if not hasattr(self, '_semantic_parent'): + self._semantic_parent = conf.lib.clang_getCursorSemanticParent(self) + + return self._semantic_parent + + @property + def lexical_parent(self): + """Return the lexical parent for this cursor.""" + if not hasattr(self, '_lexical_parent'): + self._lexical_parent = conf.lib.clang_getCursorLexicalParent(self) + + return self._lexical_parent + + @property + def translation_unit(self): + """Returns the TranslationUnit to which this Cursor belongs.""" + # If this triggers an AttributeError, the instance was not properly + # created. + return self._tu + + @property + def referenced(self): + """ + For a cursor that is a reference, returns a cursor + representing the entity that it references. + """ + if not hasattr(self, '_referenced'): + self._referenced = conf.lib.clang_getCursorReferenced(self) + + return self._referenced + + @property + def brief_comment(self): + """Returns the brief comment text associated with that Cursor""" + return conf.lib.clang_Cursor_getBriefCommentText(self) + + @property + def raw_comment(self): + """Returns the raw comment text associated with that Cursor""" + return conf.lib.clang_Cursor_getRawCommentText(self) + + def get_arguments(self): + """Return an iterator for accessing the arguments of this cursor.""" + num_args = conf.lib.clang_Cursor_getNumArguments(self) + for i in range(0, num_args): + yield conf.lib.clang_Cursor_getArgument(self, i) + + def get_num_template_arguments(self): + """Returns the number of template args associated with this cursor.""" + return conf.lib.clang_Cursor_getNumTemplateArguments(self) + + def get_template_argument_kind(self, num): + """Returns the TemplateArgumentKind for the indicated template + argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentKind(self, num) + + def get_template_argument_type(self, num): + """Returns the CXType for the indicated template argument.""" + return conf.lib.clang_Cursor_getTemplateArgumentType(self, num) + + def get_template_argument_value(self, num): + """Returns the value of the indicated arg as a signed 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentValue(self, num) + + def get_template_argument_unsigned_value(self, num): + """Returns the value of the indicated arg as an unsigned 64b integer.""" + return conf.lib.clang_Cursor_getTemplateArgumentUnsignedValue(self, num) + + def get_children(self): + """Return an iterator for accessing the children of this cursor.""" + + # FIXME: Expose iteration from CIndex, PR6125. + def visitor(child, parent, children): + # FIXME: Document this assertion in API. + # FIXME: There should just be an isNull method. + assert child != conf.lib.clang_getNullCursor() + + # Create reference to TU so it isn't GC'd before Cursor. + child._tu = self._tu + children.append(child) + return 1 # continue + children = [] + conf.lib.clang_visitChildren(self, callbacks['cursor_visit'](visitor), + children) + return iter(children) + + def walk_preorder(self): + """Depth-first preorder walk over the cursor and its descendants. + + Yields cursors. + """ + yield self + for child in self.get_children(): + for descendant in child.walk_preorder(): + yield descendant + + def get_tokens(self): + """Obtain Token instances formulating that compose this Cursor. + + This is a generator for Token instances. It returns all tokens which + occupy the extent this cursor occupies. + """ + return TokenGroup.get_tokens(self._tu, self.extent) + + def get_field_offsetof(self): + """Returns the offsetof the FIELD_DECL pointed by this Cursor.""" + return conf.lib.clang_Cursor_getOffsetOfField(self) + + def is_anonymous(self): + """ + Check if the record is anonymous. + """ + if self.kind == CursorKind.FIELD_DECL: + return self.type.get_declaration().is_anonymous() + return conf.lib.clang_Cursor_isAnonymous(self) + + def is_bitfield(self): + """ + Check if the field is a bitfield. + """ + return conf.lib.clang_Cursor_isBitField(self) + + def get_bitfield_width(self): + """ + Retrieve the width of a bitfield. + """ + return conf.lib.clang_getFieldDeclBitWidth(self) + + @property + def specialized_cursor_template(self): + """ + Retrieve the specialized cursor template. + """ + return conf.lib.clang_getSpecializedCursorTemplate(self) + + @property + def template_cursor_kind(self): + """ + Retrieve the template cursor kind. + """ + return conf.lib.clang_getTemplateCursorKind(self) + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, Cursor) + # FIXME: There should just be an isNull method. + if res == conf.lib.clang_getNullCursor(): + return None + + # Store a reference to the TU in the Python object so it won't get GC'd + # before the Cursor. + tu = None + for arg in args: + if isinstance(arg, TranslationUnit): + tu = arg + break + + if hasattr(arg, 'translation_unit'): + tu = arg.translation_unit + break + + assert tu is not None + + res._tu = tu + return res + + @staticmethod + def from_cursor_result(res, fn, args): + assert isinstance(res, Cursor) + if res == conf.lib.clang_getNullCursor(): + return None + + res._tu = args[0]._tu + return res class StorageClass(object): - """ - Describes the storage class of a declaration - """ - - # The unique kind objects, index by id. - _kinds = [] - _name_map = None - - def __init__(self, value): - if value >= len(StorageClass._kinds): - StorageClass._kinds += [None] * (value - len(StorageClass._kinds) + 1) - if StorageClass._kinds[value] is not None: - raise ValueError,'StorageClass already loaded' - self.value = value - StorageClass._kinds[value] = self - StorageClass._name_map = None - - def from_param(self): - return self.value - - @property - def name(self): - """Get the enumeration name of this storage class.""" - if self._name_map is None: - self._name_map = {} - for key,value in StorageClass.__dict__.items(): - if isinstance(value,StorageClass): - self._name_map[value] = key - return self._name_map[self] - - @staticmethod - def from_id(id): - if id >= len(StorageClass._kinds) or not StorageClass._kinds[id]: - raise ValueError,'Unknown storage class %d' % id - return StorageClass._kinds[id] - - def __repr__(self): - return 'StorageClass.%s' % (self.name,) + """ + Describes the storage class of a declaration + """ + + # The unique kind objects, index by id. + _kinds = [] + _name_map = None + + def __init__(self, value): + if value >= len(StorageClass._kinds): + StorageClass._kinds += [None] * (value - len(StorageClass._kinds) + 1) + if StorageClass._kinds[value] is not None: + raise ValueError,'StorageClass already loaded' + self.value = value + StorageClass._kinds[value] = self + StorageClass._name_map = None + + def from_param(self): + return self.value + + @property + def name(self): + """Get the enumeration name of this storage class.""" + if self._name_map is None: + self._name_map = {} + for key,value in StorageClass.__dict__.items(): + if isinstance(value,StorageClass): + self._name_map[value] = key + return self._name_map[self] + + @staticmethod + def from_id(id): + if id >= len(StorageClass._kinds) or not StorageClass._kinds[id]: + raise ValueError,'Unknown storage class %d' % id + return StorageClass._kinds[id] + + def __repr__(self): + return 'StorageClass.%s' % (self.name,) StorageClass.INVALID = StorageClass(0) StorageClass.NONE = StorageClass(1) @@ -1713,19 +1713,19 @@ def __repr__(self): ### C++ access specifiers ### class AccessSpecifier(BaseEnumeration): - """ - Describes the access of a C++ class member - """ + """ + Describes the access of a C++ class member + """ - # The unique kind objects, index by id. - _kinds = [] - _name_map = None + # The unique kind objects, index by id. + _kinds = [] + _name_map = None - def from_param(self): - return self.value + def from_param(self): + return self.value - def __repr__(self): - return 'AccessSpecifier.%s' % (self.name,) + def __repr__(self): + return 'AccessSpecifier.%s' % (self.name,) AccessSpecifier.INVALID = AccessSpecifier(0) AccessSpecifier.PUBLIC = AccessSpecifier(1) @@ -1736,21 +1736,21 @@ def __repr__(self): ### Type Kinds ### class TypeKind(BaseEnumeration): - """ - Describes the kind of type. - """ + """ + Describes the kind of type. + """ - # The unique kind objects, indexed by id. - _kinds = [] - _name_map = None + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None - @property - def spelling(self): - """Retrieve the spelling of this TypeKind.""" - return conf.lib.clang_getTypeKindSpelling(self.value) + @property + def spelling(self): + """Retrieve the spelling of this TypeKind.""" + return conf.lib.clang_getTypeKindSpelling(self.value) - def __repr__(self): - return 'TypeKind.%s' % (self.name,) + def __repr__(self): + return 'TypeKind.%s' % (self.name,) TypeKind.INVALID = TypeKind(0) TypeKind.UNEXPOSED = TypeKind(1) @@ -1805,261 +1805,261 @@ def __repr__(self): TypeKind.ELABORATED = TypeKind(119) class RefQualifierKind(BaseEnumeration): - """Describes a specific ref-qualifier of a type.""" + """Describes a specific ref-qualifier of a type.""" - # The unique kind objects, indexed by id. - _kinds = [] - _name_map = None + # The unique kind objects, indexed by id. + _kinds = [] + _name_map = None - def from_param(self): - return self.value + def from_param(self): + return self.value - def __repr__(self): - return 'RefQualifierKind.%s' % (self.name,) + def __repr__(self): + return 'RefQualifierKind.%s' % (self.name,) RefQualifierKind.NONE = RefQualifierKind(0) RefQualifierKind.LVALUE = RefQualifierKind(1) RefQualifierKind.RVALUE = RefQualifierKind(2) class Type(Structure): - """ - The type of an element in the abstract syntax tree. - """ - _fields_ = [("_kind_id", c_int), ("data", c_void_p * 2)] - - @property - def kind(self): - """Return the kind of this type.""" - return TypeKind.from_id(self._kind_id) - - def argument_types(self): - """Retrieve a container for the non-variadic arguments for this type. - - The returned object is iterable and indexable. Each item in the - container is a Type instance. - """ - class ArgumentsIterator(collections.Sequence): - def __init__(self, parent): - self.parent = parent - self.length = None - - def __len__(self): - if self.length is None: - self.length = conf.lib.clang_getNumArgTypes(self.parent) - - return self.length - - def __getitem__(self, key): - # FIXME Support slice objects. - if not isinstance(key, int): - raise TypeError("Must supply a non-negative int.") - - if key < 0: - raise IndexError("Only non-negative indexes are accepted.") - - if key >= len(self): - raise IndexError("Index greater than container length: " - "%d > %d" % ( key, len(self) )) + """ + The type of an element in the abstract syntax tree. + """ + _fields_ = [("_kind_id", c_int), ("data", c_void_p * 2)] + + @property + def kind(self): + """Return the kind of this type.""" + return TypeKind.from_id(self._kind_id) + + def argument_types(self): + """Retrieve a container for the non-variadic arguments for this type. + + The returned object is iterable and indexable. Each item in the + container is a Type instance. + """ + class ArgumentsIterator(collections.Sequence): + def __init__(self, parent): + self.parent = parent + self.length = None + + def __len__(self): + if self.length is None: + self.length = conf.lib.clang_getNumArgTypes(self.parent) + + return self.length + + def __getitem__(self, key): + # FIXME Support slice objects. + if not isinstance(key, int): + raise TypeError("Must supply a non-negative int.") + + if key < 0: + raise IndexError("Only non-negative indexes are accepted.") + + if key >= len(self): + raise IndexError("Index greater than container length: " + "%d > %d" % ( key, len(self) )) - result = conf.lib.clang_getArgType(self.parent, key) - if result.kind == TypeKind.INVALID: - raise IndexError("Argument could not be retrieved.") + result = conf.lib.clang_getArgType(self.parent, key) + if result.kind == TypeKind.INVALID: + raise IndexError("Argument could not be retrieved.") - return result - - assert self.kind == TypeKind.FUNCTIONPROTO - return ArgumentsIterator(self) - - @property - def element_type(self): - """Retrieve the Type of elements within this Type. - - If accessed on a type that is not an array, complex, or vector type, an - exception will be raised. - """ - result = conf.lib.clang_getElementType(self) - if result.kind == TypeKind.INVALID: - raise Exception('Element type not available on this type.') - - return result - - @property - def element_count(self): - """Retrieve the number of elements in this type. - - Returns an int. - - If the Type is not an array or vector, this raises. - """ - result = conf.lib.clang_getNumElements(self) - if result < 0: - raise Exception('Type does not have elements.') - - return result - - @property - def translation_unit(self): - """The TranslationUnit to which this Type is associated.""" - # If this triggers an AttributeError, the instance was not properly - # instantiated. - return self._tu - - @staticmethod - def from_result(res, fn, args): - assert isinstance(res, Type) - - tu = None - for arg in args: - if hasattr(arg, 'translation_unit'): - tu = arg.translation_unit - break - - assert tu is not None - res._tu = tu - - return res - - def get_canonical(self): - """ - Return the canonical type for a Type. - - Clang's type system explicitly models typedefs and all the - ways a specific type can be represented. The canonical type - is the underlying type with all the "sugar" removed. For - example, if 'T' is a typedef for 'int', the canonical type for - 'T' would be 'int'. - """ - return conf.lib.clang_getCanonicalType(self) - - def is_const_qualified(self): - """Determine whether a Type has the "const" qualifier set. - - This does not look through typedefs that may have added "const" - at a different level. - """ - return conf.lib.clang_isConstQualifiedType(self) - - def is_volatile_qualified(self): - """Determine whether a Type has the "volatile" qualifier set. - - This does not look through typedefs that may have added "volatile" - at a different level. - """ - return conf.lib.clang_isVolatileQualifiedType(self) - - def is_restrict_qualified(self): - """Determine whether a Type has the "restrict" qualifier set. - - This does not look through typedefs that may have added "restrict" at - a different level. - """ - return conf.lib.clang_isRestrictQualifiedType(self) - - def is_function_variadic(self): - """Determine whether this function Type is a variadic function type.""" - assert self.kind == TypeKind.FUNCTIONPROTO - - return conf.lib.clang_isFunctionTypeVariadic(self) - - def is_pod(self): - """Determine whether this Type represents plain old data (POD).""" - return conf.lib.clang_isPODType(self) - - def get_pointee(self): - """ - For pointer types, returns the type of the pointee. - """ - return conf.lib.clang_getPointeeType(self) - - def get_declaration(self): - """ - Return the cursor for the declaration of the given type. - """ - return conf.lib.clang_getTypeDeclaration(self) - - def get_result(self): - """ - Retrieve the result type associated with a function type. - """ - return conf.lib.clang_getResultType(self) - - def get_array_element_type(self): - """ - Retrieve the type of the elements of the array type. - """ - return conf.lib.clang_getArrayElementType(self) - - def get_array_size(self): - """ - Retrieve the size of the constant array. - """ - return conf.lib.clang_getArraySize(self) - - def get_class_type(self): - """ - Retrieve the class type of the member pointer type. - """ - return conf.lib.clang_Type_getClassType(self) - - def get_named_type(self): - """ - Retrieve the type named by the qualified-id. - """ - return conf.lib.clang_Type_getNamedType(self) - - def get_align(self): - """ - Retrieve the alignment of the record. - """ - return conf.lib.clang_Type_getAlignOf(self) - - def get_size(self): - """ - Retrieve the size of the record. - """ - return conf.lib.clang_Type_getSizeOf(self) - - def get_offset(self, fieldname): - """ - Retrieve the offset of a field in the record. - """ - return conf.lib.clang_Type_getOffsetOf(self, c_char_p(fieldname)) - - def get_ref_qualifier(self): - """ - Retrieve the ref-qualifier of the type. - """ - return RefQualifierKind.from_id( - conf.lib.clang_Type_getCXXRefQualifier(self)) - - def get_fields(self): - """Return an iterator for accessing the fields of this type.""" - - def visitor(field, children): - assert field != conf.lib.clang_getNullCursor() - - # Create reference to TU so it isn't GC'd before Cursor. - field._tu = self._tu - fields.append(field) - return 1 # continue - fields = [] - conf.lib.clang_Type_visitFields(self, - callbacks['fields_visit'](visitor), fields) - return iter(fields) - - @property - def spelling(self): - """Retrieve the spelling of this Type.""" - return conf.lib.clang_getTypeSpelling(self) - - def __eq__(self, other): - if type(other) != type(self): - return False - - return conf.lib.clang_equalTypes(self, other) - - def __ne__(self, other): - return not self.__eq__(other) + return result + + assert self.kind == TypeKind.FUNCTIONPROTO + return ArgumentsIterator(self) + + @property + def element_type(self): + """Retrieve the Type of elements within this Type. + + If accessed on a type that is not an array, complex, or vector type, an + exception will be raised. + """ + result = conf.lib.clang_getElementType(self) + if result.kind == TypeKind.INVALID: + raise Exception('Element type not available on this type.') + + return result + + @property + def element_count(self): + """Retrieve the number of elements in this type. + + Returns an int. + + If the Type is not an array or vector, this raises. + """ + result = conf.lib.clang_getNumElements(self) + if result < 0: + raise Exception('Type does not have elements.') + + return result + + @property + def translation_unit(self): + """The TranslationUnit to which this Type is associated.""" + # If this triggers an AttributeError, the instance was not properly + # instantiated. + return self._tu + + @staticmethod + def from_result(res, fn, args): + assert isinstance(res, Type) + + tu = None + for arg in args: + if hasattr(arg, 'translation_unit'): + tu = arg.translation_unit + break + + assert tu is not None + res._tu = tu + + return res + + def get_canonical(self): + """ + Return the canonical type for a Type. + + Clang's type system explicitly models typedefs and all the + ways a specific type can be represented. The canonical type + is the underlying type with all the "sugar" removed. For + example, if 'T' is a typedef for 'int', the canonical type for + 'T' would be 'int'. + """ + return conf.lib.clang_getCanonicalType(self) + + def is_const_qualified(self): + """Determine whether a Type has the "const" qualifier set. + + This does not look through typedefs that may have added "const" + at a different level. + """ + return conf.lib.clang_isConstQualifiedType(self) + + def is_volatile_qualified(self): + """Determine whether a Type has the "volatile" qualifier set. + + This does not look through typedefs that may have added "volatile" + at a different level. + """ + return conf.lib.clang_isVolatileQualifiedType(self) + + def is_restrict_qualified(self): + """Determine whether a Type has the "restrict" qualifier set. + + This does not look through typedefs that may have added "restrict" at + a different level. + """ + return conf.lib.clang_isRestrictQualifiedType(self) + + def is_function_variadic(self): + """Determine whether this function Type is a variadic function type.""" + assert self.kind == TypeKind.FUNCTIONPROTO + + return conf.lib.clang_isFunctionTypeVariadic(self) + + def is_pod(self): + """Determine whether this Type represents plain old data (POD).""" + return conf.lib.clang_isPODType(self) + + def get_pointee(self): + """ + For pointer types, returns the type of the pointee. + """ + return conf.lib.clang_getPointeeType(self) + + def get_declaration(self): + """ + Return the cursor for the declaration of the given type. + """ + return conf.lib.clang_getTypeDeclaration(self) + + def get_result(self): + """ + Retrieve the result type associated with a function type. + """ + return conf.lib.clang_getResultType(self) + + def get_array_element_type(self): + """ + Retrieve the type of the elements of the array type. + """ + return conf.lib.clang_getArrayElementType(self) + + def get_array_size(self): + """ + Retrieve the size of the constant array. + """ + return conf.lib.clang_getArraySize(self) + + def get_class_type(self): + """ + Retrieve the class type of the member pointer type. + """ + return conf.lib.clang_Type_getClassType(self) + + def get_named_type(self): + """ + Retrieve the type named by the qualified-id. + """ + return conf.lib.clang_Type_getNamedType(self) + + def get_align(self): + """ + Retrieve the alignment of the record. + """ + return conf.lib.clang_Type_getAlignOf(self) + + def get_size(self): + """ + Retrieve the size of the record. + """ + return conf.lib.clang_Type_getSizeOf(self) + + def get_offset(self, fieldname): + """ + Retrieve the offset of a field in the record. + """ + return conf.lib.clang_Type_getOffsetOf(self, c_char_p(fieldname)) + + def get_ref_qualifier(self): + """ + Retrieve the ref-qualifier of the type. + """ + return RefQualifierKind.from_id( + conf.lib.clang_Type_getCXXRefQualifier(self)) + + def get_fields(self): + """Return an iterator for accessing the fields of this type.""" + + def visitor(field, children): + assert field != conf.lib.clang_getNullCursor() + + # Create reference to TU so it isn't GC'd before Cursor. + field._tu = self._tu + fields.append(field) + return 1 # continue + fields = [] + conf.lib.clang_Type_visitFields(self, + callbacks['fields_visit'](visitor), fields) + return iter(fields) + + @property + def spelling(self): + """Retrieve the spelling of this Type.""" + return conf.lib.clang_getTypeSpelling(self) + + def __eq__(self, other): + if type(other) != type(self): + return False + + return conf.lib.clang_equalTypes(self, other) + + def __ne__(self, other): + return not self.__eq__(other) ## CIndex Objects ## @@ -2068,889 +2068,889 @@ def __ne__(self, other): # a void*. class ClangObject(object): - """ - A helper for Clang objects. This class helps act as an intermediary for - the ctypes library and the Clang CIndex library. - """ - def __init__(self, obj): - assert isinstance(obj, c_object_p) and obj - self.obj = self._as_parameter_ = obj + """ + A helper for Clang objects. This class helps act as an intermediary for + the ctypes library and the Clang CIndex library. + """ + def __init__(self, obj): + assert isinstance(obj, c_object_p) and obj + self.obj = self._as_parameter_ = obj - def from_param(self): - return self._as_parameter_ + def from_param(self): + return self._as_parameter_ class _CXUnsavedFile(Structure): - """Helper for passing unsaved file arguments.""" - _fields_ = [("name", c_char_p), ("contents", c_char_p), ('length', c_ulong)] + """Helper for passing unsaved file arguments.""" + _fields_ = [("name", c_char_p), ("contents", c_char_p), ('length', c_ulong)] # Functions calls through the python interface are rather slow. Fortunately, # for most symboles, we do not need to perform a function call. Their spelling # never changes and is consequently provided by this spelling cache. SpellingCache = { - # 0: CompletionChunk.Kind("Optional"), - # 1: CompletionChunk.Kind("TypedText"), - # 2: CompletionChunk.Kind("Text"), - # 3: CompletionChunk.Kind("Placeholder"), - # 4: CompletionChunk.Kind("Informative"), - # 5 : CompletionChunk.Kind("CurrentParameter"), - 6: '(', # CompletionChunk.Kind("LeftParen"), - 7: ')', # CompletionChunk.Kind("RightParen"), - 8: '[', # CompletionChunk.Kind("LeftBracket"), - 9: ']', # CompletionChunk.Kind("RightBracket"), - 10: '{', # CompletionChunk.Kind("LeftBrace"), - 11: '}', # CompletionChunk.Kind("RightBrace"), - 12: '<', # CompletionChunk.Kind("LeftAngle"), - 13: '>', # CompletionChunk.Kind("RightAngle"), - 14: ', ', # CompletionChunk.Kind("Comma"), - # 15: CompletionChunk.Kind("ResultType"), - 16: ':', # CompletionChunk.Kind("Colon"), - 17: ';', # CompletionChunk.Kind("SemiColon"), - 18: '=', # CompletionChunk.Kind("Equal"), - 19: ' ', # CompletionChunk.Kind("HorizontalSpace"), - # 20: CompletionChunk.Kind("VerticalSpace") + # 0: CompletionChunk.Kind("Optional"), + # 1: CompletionChunk.Kind("TypedText"), + # 2: CompletionChunk.Kind("Text"), + # 3: CompletionChunk.Kind("Placeholder"), + # 4: CompletionChunk.Kind("Informative"), + # 5 : CompletionChunk.Kind("CurrentParameter"), + 6: '(', # CompletionChunk.Kind("LeftParen"), + 7: ')', # CompletionChunk.Kind("RightParen"), + 8: '[', # CompletionChunk.Kind("LeftBracket"), + 9: ']', # CompletionChunk.Kind("RightBracket"), + 10: '{', # CompletionChunk.Kind("LeftBrace"), + 11: '}', # CompletionChunk.Kind("RightBrace"), + 12: '<', # CompletionChunk.Kind("LeftAngle"), + 13: '>', # CompletionChunk.Kind("RightAngle"), + 14: ', ', # CompletionChunk.Kind("Comma"), + # 15: CompletionChunk.Kind("ResultType"), + 16: ':', # CompletionChunk.Kind("Colon"), + 17: ';', # CompletionChunk.Kind("SemiColon"), + 18: '=', # CompletionChunk.Kind("Equal"), + 19: ' ', # CompletionChunk.Kind("HorizontalSpace"), + # 20: CompletionChunk.Kind("VerticalSpace") } class CompletionChunk: - class Kind: - def __init__(self, name): - self.name = name + class Kind: + def __init__(self, name): + self.name = name - def __str__(self): - return self.name + def __str__(self): + return self.name - def __repr__(self): - return "" % self + def __repr__(self): + return "" % self - def __init__(self, completionString, key): - self.cs = completionString - self.key = key - self.__kindNumberCache = -1 + def __init__(self, completionString, key): + self.cs = completionString + self.key = key + self.__kindNumberCache = -1 - def __repr__(self): - return "{'" + self.spelling + "', " + str(self.kind) + "}" + def __repr__(self): + return "{'" + self.spelling + "', " + str(self.kind) + "}" - @CachedProperty - def spelling(self): - if self.__kindNumber in SpellingCache: - return SpellingCache[self.__kindNumber] - return conf.lib.clang_getCompletionChunkText(self.cs, self.key).spelling + @CachedProperty + def spelling(self): + if self.__kindNumber in SpellingCache: + return SpellingCache[self.__kindNumber] + return conf.lib.clang_getCompletionChunkText(self.cs, self.key).spelling - # We do not use @CachedProperty here, as the manual implementation is - # apparently still significantly faster. Please profile carefully if you - # would like to add CachedProperty back. - @property - def __kindNumber(self): - if self.__kindNumberCache == -1: - self.__kindNumberCache = \ - conf.lib.clang_getCompletionChunkKind(self.cs, self.key) - return self.__kindNumberCache + # We do not use @CachedProperty here, as the manual implementation is + # apparently still significantly faster. Please profile carefully if you + # would like to add CachedProperty back. + @property + def __kindNumber(self): + if self.__kindNumberCache == -1: + self.__kindNumberCache = \ + conf.lib.clang_getCompletionChunkKind(self.cs, self.key) + return self.__kindNumberCache - @CachedProperty - def kind(self): - return completionChunkKindMap[self.__kindNumber] + @CachedProperty + def kind(self): + return completionChunkKindMap[self.__kindNumber] - @CachedProperty - def string(self): - res = conf.lib.clang_getCompletionChunkCompletionString(self.cs, - self.key) + @CachedProperty + def string(self): + res = conf.lib.clang_getCompletionChunkCompletionString(self.cs, + self.key) - if (res): - return CompletionString(res) - else: - None + if (res): + return CompletionString(res) + else: + None - def isKindOptional(self): - return self.__kindNumber == 0 + def isKindOptional(self): + return self.__kindNumber == 0 - def isKindTypedText(self): - return self.__kindNumber == 1 + def isKindTypedText(self): + return self.__kindNumber == 1 - def isKindPlaceHolder(self): - return self.__kindNumber == 3 + def isKindPlaceHolder(self): + return self.__kindNumber == 3 - def isKindInformative(self): - return self.__kindNumber == 4 + def isKindInformative(self): + return self.__kindNumber == 4 - def isKindResultType(self): - return self.__kindNumber == 15 + def isKindResultType(self): + return self.__kindNumber == 15 completionChunkKindMap = { - 0: CompletionChunk.Kind("Optional"), - 1: CompletionChunk.Kind("TypedText"), - 2: CompletionChunk.Kind("Text"), - 3: CompletionChunk.Kind("Placeholder"), - 4: CompletionChunk.Kind("Informative"), - 5: CompletionChunk.Kind("CurrentParameter"), - 6: CompletionChunk.Kind("LeftParen"), - 7: CompletionChunk.Kind("RightParen"), - 8: CompletionChunk.Kind("LeftBracket"), - 9: CompletionChunk.Kind("RightBracket"), - 10: CompletionChunk.Kind("LeftBrace"), - 11: CompletionChunk.Kind("RightBrace"), - 12: CompletionChunk.Kind("LeftAngle"), - 13: CompletionChunk.Kind("RightAngle"), - 14: CompletionChunk.Kind("Comma"), - 15: CompletionChunk.Kind("ResultType"), - 16: CompletionChunk.Kind("Colon"), - 17: CompletionChunk.Kind("SemiColon"), - 18: CompletionChunk.Kind("Equal"), - 19: CompletionChunk.Kind("HorizontalSpace"), - 20: CompletionChunk.Kind("VerticalSpace")} + 0: CompletionChunk.Kind("Optional"), + 1: CompletionChunk.Kind("TypedText"), + 2: CompletionChunk.Kind("Text"), + 3: CompletionChunk.Kind("Placeholder"), + 4: CompletionChunk.Kind("Informative"), + 5: CompletionChunk.Kind("CurrentParameter"), + 6: CompletionChunk.Kind("LeftParen"), + 7: CompletionChunk.Kind("RightParen"), + 8: CompletionChunk.Kind("LeftBracket"), + 9: CompletionChunk.Kind("RightBracket"), + 10: CompletionChunk.Kind("LeftBrace"), + 11: CompletionChunk.Kind("RightBrace"), + 12: CompletionChunk.Kind("LeftAngle"), + 13: CompletionChunk.Kind("RightAngle"), + 14: CompletionChunk.Kind("Comma"), + 15: CompletionChunk.Kind("ResultType"), + 16: CompletionChunk.Kind("Colon"), + 17: CompletionChunk.Kind("SemiColon"), + 18: CompletionChunk.Kind("Equal"), + 19: CompletionChunk.Kind("HorizontalSpace"), + 20: CompletionChunk.Kind("VerticalSpace")} class CompletionString(ClangObject): - class Availability: - def __init__(self, name): - self.name = name + class Availability: + def __init__(self, name): + self.name = name - def __str__(self): - return self.name + def __str__(self): + return self.name - def __repr__(self): - return "" % self + def __repr__(self): + return "" % self - def __len__(self): - return self.num_chunks + def __len__(self): + return self.num_chunks - @CachedProperty - def num_chunks(self): - return conf.lib.clang_getNumCompletionChunks(self.obj) + @CachedProperty + def num_chunks(self): + return conf.lib.clang_getNumCompletionChunks(self.obj) - def __getitem__(self, key): - if self.num_chunks <= key: - raise IndexError - return CompletionChunk(self.obj, key) + def __getitem__(self, key): + if self.num_chunks <= key: + raise IndexError + return CompletionChunk(self.obj, key) - @property - def priority(self): - return conf.lib.clang_getCompletionPriority(self.obj) + @property + def priority(self): + return conf.lib.clang_getCompletionPriority(self.obj) - @property - def availability(self): - res = conf.lib.clang_getCompletionAvailability(self.obj) - return availabilityKinds[res] + @property + def availability(self): + res = conf.lib.clang_getCompletionAvailability(self.obj) + return availabilityKinds[res] - @property - def briefComment(self): - if conf.function_exists("clang_getCompletionBriefComment"): - return conf.lib.clang_getCompletionBriefComment(self.obj) - return _CXString() + @property + def briefComment(self): + if conf.function_exists("clang_getCompletionBriefComment"): + return conf.lib.clang_getCompletionBriefComment(self.obj) + return _CXString() - def __repr__(self): - return " | ".join([str(a) for a in self]) \ - + " || Priority: " + str(self.priority) \ - + " || Availability: " + str(self.availability) \ - + " || Brief comment: " + str(self.briefComment.spelling) + def __repr__(self): + return " | ".join([str(a) for a in self]) \ + + " || Priority: " + str(self.priority) \ + + " || Availability: " + str(self.availability) \ + + " || Brief comment: " + str(self.briefComment.spelling) availabilityKinds = { - 0: CompletionChunk.Kind("Available"), - 1: CompletionChunk.Kind("Deprecated"), - 2: CompletionChunk.Kind("NotAvailable"), - 3: CompletionChunk.Kind("NotAccessible")} + 0: CompletionChunk.Kind("Available"), + 1: CompletionChunk.Kind("Deprecated"), + 2: CompletionChunk.Kind("NotAvailable"), + 3: CompletionChunk.Kind("NotAccessible")} class CodeCompletionResult(Structure): - _fields_ = [('cursorKind', c_int), ('completionString', c_object_p)] + _fields_ = [('cursorKind', c_int), ('completionString', c_object_p)] - def __repr__(self): - return str(CompletionString(self.completionString)) + def __repr__(self): + return str(CompletionString(self.completionString)) - @property - def kind(self): - return CursorKind.from_id(self.cursorKind) + @property + def kind(self): + return CursorKind.from_id(self.cursorKind) - @property - def string(self): - return CompletionString(self.completionString) + @property + def string(self): + return CompletionString(self.completionString) class CCRStructure(Structure): - _fields_ = [('results', POINTER(CodeCompletionResult)), - ('numResults', c_int)] + _fields_ = [('results', POINTER(CodeCompletionResult)), + ('numResults', c_int)] - def __len__(self): - return self.numResults + def __len__(self): + return self.numResults - def __getitem__(self, key): - if len(self) <= key: - raise IndexError + def __getitem__(self, key): + if len(self) <= key: + raise IndexError - return self.results[key] + return self.results[key] class CodeCompletionResults(ClangObject): - def __init__(self, ptr): - assert isinstance(ptr, POINTER(CCRStructure)) and ptr - self.ptr = self._as_parameter_ = ptr + def __init__(self, ptr): + assert isinstance(ptr, POINTER(CCRStructure)) and ptr + self.ptr = self._as_parameter_ = ptr - def from_param(self): - return self._as_parameter_ + def from_param(self): + return self._as_parameter_ - def __del__(self): - conf.lib.clang_disposeCodeCompleteResults(self) + def __del__(self): + conf.lib.clang_disposeCodeCompleteResults(self) - @property - def results(self): - return self.ptr.contents + @property + def results(self): + return self.ptr.contents - @property - def diagnostics(self): - class DiagnosticsItr: - def __init__(self, ccr): - self.ccr= ccr + @property + def diagnostics(self): + class DiagnosticsItr: + def __init__(self, ccr): + self.ccr= ccr - def __len__(self): - return int(\ - conf.lib.clang_codeCompleteGetNumDiagnostics(self.ccr)) + def __len__(self): + return int(\ + conf.lib.clang_codeCompleteGetNumDiagnostics(self.ccr)) - def __getitem__(self, key): - return conf.lib.clang_codeCompleteGetDiagnostic(self.ccr, key) + def __getitem__(self, key): + return conf.lib.clang_codeCompleteGetDiagnostic(self.ccr, key) - return DiagnosticsItr(self) + return DiagnosticsItr(self) class Index(ClangObject): - """ - The Index type provides the primary interface to the Clang CIndex library, - primarily by providing an interface for reading and parsing translation - units. - """ - - @staticmethod - def create(excludeDecls=False): - """ - Create a new Index. - Parameters: - excludeDecls -- Exclude local declarations from translation units. - """ - return Index(conf.lib.clang_createIndex(excludeDecls, 0)) - - def __del__(self): - conf.lib.clang_disposeIndex(self) - - def read(self, path): - """Load a TranslationUnit from the given AST file.""" - return TranslationUnit.from_ast_file(path, self) - - def parse(self, path, args=None, unsaved_files=None, options = 0): - """Load the translation unit from the given source code file by running - clang and generating the AST before loading. Additional command line - parameters can be passed to clang via the args parameter. - - In-memory contents for files can be provided by passing a list of pairs - to as unsaved_files, the first item should be the filenames to be mapped - and the second should be the contents to be substituted for the - file. The contents may be passed as strings or file objects. - - If an error was encountered during parsing, a TranslationUnitLoadError - will be raised. - """ - return TranslationUnit.from_source(path, args, unsaved_files, options, - self) + """ + The Index type provides the primary interface to the Clang CIndex library, + primarily by providing an interface for reading and parsing translation + units. + """ + + @staticmethod + def create(excludeDecls=False): + """ + Create a new Index. + Parameters: + excludeDecls -- Exclude local declarations from translation units. + """ + return Index(conf.lib.clang_createIndex(excludeDecls, 0)) + + def __del__(self): + conf.lib.clang_disposeIndex(self) + + def read(self, path): + """Load a TranslationUnit from the given AST file.""" + return TranslationUnit.from_ast_file(path, self) + + def parse(self, path, args=None, unsaved_files=None, options = 0): + """Load the translation unit from the given source code file by running + clang and generating the AST before loading. Additional command line + parameters can be passed to clang via the args parameter. + + In-memory contents for files can be provided by passing a list of pairs + to as unsaved_files, the first item should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + + If an error was encountered during parsing, a TranslationUnitLoadError + will be raised. + """ + return TranslationUnit.from_source(path, args, unsaved_files, options, + self) class TranslationUnit(ClangObject): - """Represents a source code translation unit. + """Represents a source code translation unit. - This is one of the main types in the API. Any time you wish to interact - with Clang's representation of a source file, you typically start with a - translation unit. - """ + This is one of the main types in the API. Any time you wish to interact + with Clang's representation of a source file, you typically start with a + translation unit. + """ - # Default parsing mode. - PARSE_NONE = 0 + # Default parsing mode. + PARSE_NONE = 0 - # Instruct the parser to create a detailed processing record containing - # metadata not normally retained. - PARSE_DETAILED_PROCESSING_RECORD = 1 + # Instruct the parser to create a detailed processing record containing + # metadata not normally retained. + PARSE_DETAILED_PROCESSING_RECORD = 1 - # Indicates that the translation unit is incomplete. This is typically used - # when parsing headers. - PARSE_INCOMPLETE = 2 + # Indicates that the translation unit is incomplete. This is typically used + # when parsing headers. + PARSE_INCOMPLETE = 2 - # Instruct the parser to create a pre-compiled preamble for the translation - # unit. This caches the preamble (included files at top of source file). - # This is useful if the translation unit will be reparsed and you don't - # want to incur the overhead of reparsing the preamble. - PARSE_PRECOMPILED_PREAMBLE = 4 + # Instruct the parser to create a pre-compiled preamble for the translation + # unit. This caches the preamble (included files at top of source file). + # This is useful if the translation unit will be reparsed and you don't + # want to incur the overhead of reparsing the preamble. + PARSE_PRECOMPILED_PREAMBLE = 4 - # Cache code completion information on parse. This adds time to parsing but - # speeds up code completion. - PARSE_CACHE_COMPLETION_RESULTS = 8 + # Cache code completion information on parse. This adds time to parsing but + # speeds up code completion. + PARSE_CACHE_COMPLETION_RESULTS = 8 - # Flags with values 16 and 32 are deprecated and intentionally omitted. + # Flags with values 16 and 32 are deprecated and intentionally omitted. - # Do not parse function bodies. This is useful if you only care about - # searching for declarations/definitions. - PARSE_SKIP_FUNCTION_BODIES = 64 + # Do not parse function bodies. This is useful if you only care about + # searching for declarations/definitions. + PARSE_SKIP_FUNCTION_BODIES = 64 - # Used to indicate that brief documentation comments should be included - # into the set of code completions returned from this translation unit. - PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 + # Used to indicate that brief documentation comments should be included + # into the set of code completions returned from this translation unit. + PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 - @classmethod - def from_source(cls, filename, args=None, unsaved_files=None, options=0, - index=None): - """Create a TranslationUnit by parsing source. + @classmethod + def from_source(cls, filename, args=None, unsaved_files=None, options=0, + index=None): + """Create a TranslationUnit by parsing source. - This is capable of processing source code both from files on the - filesystem as well as in-memory contents. + This is capable of processing source code both from files on the + filesystem as well as in-memory contents. - Command-line arguments that would be passed to clang are specified as - a list via args. These can be used to specify include paths, warnings, - etc. e.g. ["-Wall", "-I/path/to/include"]. + Command-line arguments that would be passed to clang are specified as + a list via args. These can be used to specify include paths, warnings, + etc. e.g. ["-Wall", "-I/path/to/include"]. - In-memory file content can be provided via unsaved_files. This is an - iterable of 2-tuples. The first element is the str filename. The - second element defines the content. Content can be provided as str - source code or as file objects (anything with a read() method). If - a file object is being used, content will be read until EOF and the - read cursor will not be reset to its original position. + In-memory file content can be provided via unsaved_files. This is an + iterable of 2-tuples. The first element is the str filename. The + second element defines the content. Content can be provided as str + source code or as file objects (anything with a read() method). If + a file object is being used, content will be read until EOF and the + read cursor will not be reset to its original position. - options is a bitwise or of TranslationUnit.PARSE_XXX flags which will - control parsing behavior. + options is a bitwise or of TranslationUnit.PARSE_XXX flags which will + control parsing behavior. - index is an Index instance to utilize. If not provided, a new Index - will be created for this TranslationUnit. + index is an Index instance to utilize. If not provided, a new Index + will be created for this TranslationUnit. - To parse source from the filesystem, the filename of the file to parse - is specified by the filename argument. Or, filename could be None and - the args list would contain the filename(s) to parse. - - To parse source from an in-memory buffer, set filename to the virtual - filename you wish to associate with this source (e.g. "test.c"). The - contents of that file are then provided in unsaved_files. - - If an error occurs, a TranslationUnitLoadError is raised. - - Please note that a TranslationUnit with parser errors may be returned. - It is the caller's responsibility to check tu.diagnostics for errors. - - Also note that Clang infers the source language from the extension of - the input filename. If you pass in source code containing a C++ class - declaration with the filename "test.c" parsing will fail. - """ - if args is None: - args = [] - - if unsaved_files is None: - unsaved_files = [] - - if index is None: - index = Index.create() + To parse source from the filesystem, the filename of the file to parse + is specified by the filename argument. Or, filename could be None and + the args list would contain the filename(s) to parse. + + To parse source from an in-memory buffer, set filename to the virtual + filename you wish to associate with this source (e.g. "test.c"). The + contents of that file are then provided in unsaved_files. + + If an error occurs, a TranslationUnitLoadError is raised. + + Please note that a TranslationUnit with parser errors may be returned. + It is the caller's responsibility to check tu.diagnostics for errors. + + Also note that Clang infers the source language from the extension of + the input filename. If you pass in source code containing a C++ class + declaration with the filename "test.c" parsing will fail. + """ + if args is None: + args = [] + + if unsaved_files is None: + unsaved_files = [] + + if index is None: + index = Index.create() - args_array = None - if len(args) > 0: - args_array = (c_char_p * len(args))(* args) - - unsaved_array = None - if len(unsaved_files) > 0: - unsaved_array = (_CXUnsavedFile * len(unsaved_files))() - for i, (name, contents) in enumerate(unsaved_files): - if hasattr(contents, "read"): - contents = contents.read() + args_array = None + if len(args) > 0: + args_array = (c_char_p * len(args))(* args) + + unsaved_array = None + if len(unsaved_files) > 0: + unsaved_array = (_CXUnsavedFile * len(unsaved_files))() + for i, (name, contents) in enumerate(unsaved_files): + if hasattr(contents, "read"): + contents = contents.read() - unsaved_array[i].name = name - unsaved_array[i].contents = contents - unsaved_array[i].length = len(contents) + unsaved_array[i].name = name + unsaved_array[i].contents = contents + unsaved_array[i].length = len(contents) - ptr = conf.lib.clang_parseTranslationUnit(index, filename, args_array, - len(args), unsaved_array, - len(unsaved_files), options) + ptr = conf.lib.clang_parseTranslationUnit(index, filename, args_array, + len(args), unsaved_array, + len(unsaved_files), options) - if not ptr: - conf.unload_lib() - raise TranslationUnitLoadError("Error parsing translation unit.") + if not ptr: + conf.unload_lib() + raise TranslationUnitLoadError("Error parsing translation unit.") - return cls(ptr, index=index) + return cls(ptr, index=index) - @classmethod - def from_ast_file(cls, filename, index=None): - """Create a TranslationUnit instance from a saved AST file. + @classmethod + def from_ast_file(cls, filename, index=None): + """Create a TranslationUnit instance from a saved AST file. - A previously-saved AST file (provided with -emit-ast or - TranslationUnit.save()) is loaded from the filename specified. + A previously-saved AST file (provided with -emit-ast or + TranslationUnit.save()) is loaded from the filename specified. - If the file cannot be loaded, a TranslationUnitLoadError will be - raised. + If the file cannot be loaded, a TranslationUnitLoadError will be + raised. - index is optional and is the Index instance to use. If not provided, - a default Index will be created. - """ - if index is None: - index = Index.create() + index is optional and is the Index instance to use. If not provided, + a default Index will be created. + """ + if index is None: + index = Index.create() - ptr = conf.lib.clang_createTranslationUnit(index, filename) - if not ptr: - raise TranslationUnitLoadError(filename) + ptr = conf.lib.clang_createTranslationUnit(index, filename) + if not ptr: + raise TranslationUnitLoadError(filename) - return cls(ptr=ptr, index=index) - - def __init__(self, ptr, index): - """Create a TranslationUnit instance. + return cls(ptr=ptr, index=index) + + def __init__(self, ptr, index): + """Create a TranslationUnit instance. - TranslationUnits should be created using one of the from_* @classmethod - functions above. __init__ is only called internally. - """ - assert isinstance(index, Index) - self.index = index - ClangObject.__init__(self, ptr) + TranslationUnits should be created using one of the from_* @classmethod + functions above. __init__ is only called internally. + """ + assert isinstance(index, Index) + self.index = index + ClangObject.__init__(self, ptr) - def __del__(self): - conf.lib.clang_disposeTranslationUnit(self) + def __del__(self): + conf.lib.clang_disposeTranslationUnit(self) - @property - def cursor(self): - """Retrieve the cursor that represents the given translation unit.""" - return conf.lib.clang_getTranslationUnitCursor(self) + @property + def cursor(self): + """Retrieve the cursor that represents the given translation unit.""" + return conf.lib.clang_getTranslationUnitCursor(self) - @property - def spelling(self): - """Get the original translation unit source file name.""" - return conf.lib.clang_getTranslationUnitSpelling(self) + @property + def spelling(self): + """Get the original translation unit source file name.""" + return conf.lib.clang_getTranslationUnitSpelling(self) - def get_includes(self): - """ - Return an iterable sequence of FileInclusion objects that describe the - sequence of inclusions in a translation unit. The first object in - this sequence is always the input file. Note that this method will not - recursively iterate over header files included through precompiled - headers. - """ - def visitor(fobj, lptr, depth, includes): - if depth > 0: - loc = lptr.contents - includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) - - # Automatically adapt CIndex/ctype pointers to python objects - includes = [] - conf.lib.clang_getInclusions(self, - callbacks['translation_unit_includes'](visitor), includes) - - return iter(includes) - - def get_file(self, filename): - """Obtain a File from this translation unit.""" - - return File.from_name(self, filename) - - def get_location(self, filename, position): - """Obtain a SourceLocation for a file in this translation unit. - - The position can be specified by passing: - - - Integer file offset. Initial file offset is 0. - - 2-tuple of (line number, column number). Initial file position is - (0, 0) - """ - f = self.get_file(filename) - - if isinstance(position, int): - return SourceLocation.from_offset(self, f, position) - - return SourceLocation.from_position(self, f, position[0], position[1]) - - def get_extent(self, filename, locations): - """Obtain a SourceRange from this translation unit. - - The bounds of the SourceRange must ultimately be defined by a start and - end SourceLocation. For the locations argument, you can pass: - - - 2 SourceLocation instances in a 2-tuple or list. - - 2 int file offsets via a 2-tuple or list. - - 2 2-tuple or lists of (line, column) pairs in a 2-tuple or list. - - e.g. - - get_extent('foo.c', (5, 10)) - get_extent('foo.c', ((1, 1), (1, 15))) - """ - f = self.get_file(filename) - - if len(locations) < 2: - raise Exception('Must pass object with at least 2 elements') - - start_location, end_location = locations - - if hasattr(start_location, '__len__'): - start_location = SourceLocation.from_position(self, f, - start_location[0], start_location[1]) - elif isinstance(start_location, int): - start_location = SourceLocation.from_offset(self, f, - start_location) - - if hasattr(end_location, '__len__'): - end_location = SourceLocation.from_position(self, f, - end_location[0], end_location[1]) - elif isinstance(end_location, int): - end_location = SourceLocation.from_offset(self, f, end_location) - else: - end_location = SourceLocation.from_offset(self, f, int(end_location)) - - - assert isinstance(start_location, SourceLocation) - assert isinstance(end_location, SourceLocation) - - return SourceRange.from_locations(start_location, end_location) - - @property - def diagnostics(self): - """ - Return an iterable (and indexable) object containing the diagnostics. - """ - class DiagIterator: - def __init__(self, tu): - self.tu = tu - - def __len__(self): - return int(conf.lib.clang_getNumDiagnostics(self.tu)) - - def __getitem__(self, key): - diag = conf.lib.clang_getDiagnostic(self.tu, key) - if not diag: - raise IndexError - return Diagnostic(diag) - - return DiagIterator(self) - - def reparse(self, unsaved_files=None, options=0): - """ - Reparse an already parsed translation unit. - - In-memory contents for files can be provided by passing a list of pairs - as unsaved_files, the first items should be the filenames to be mapped - and the second should be the contents to be substituted for the - file. The contents may be passed as strings or file objects. - """ - if unsaved_files is None: - unsaved_files = [] - - unsaved_files_array = 0 - if len(unsaved_files): - unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() - for i,(name,value) in enumerate(unsaved_files): - if not isinstance(value, str): - # FIXME: It would be great to support an efficient version - # of this, one day. - value = value.read() - print value - if not isinstance(value, str): - raise TypeError,'Unexpected unsaved file contents.' - unsaved_files_array[i].name = name - unsaved_files_array[i].contents = value - unsaved_files_array[i].length = len(value) - ptr = conf.lib.clang_reparseTranslationUnit(self, len(unsaved_files), - unsaved_files_array, options) - - def save(self, filename): - """Saves the TranslationUnit to a file. - - This is equivalent to passing -emit-ast to the clang frontend. The - saved file can be loaded back into a TranslationUnit. Or, if it - corresponds to a header, it can be used as a pre-compiled header file. - - If an error occurs while saving, a TranslationUnitSaveError is raised. - If the error was TranslationUnitSaveError.ERROR_INVALID_TU, this means - the constructed TranslationUnit was not valid at time of save. In this - case, the reason(s) why should be available via - TranslationUnit.diagnostics(). - - filename -- The path to save the translation unit to. - """ - options = conf.lib.clang_defaultSaveOptions(self) - result = int(conf.lib.clang_saveTranslationUnit(self, filename, - options)) - if result != 0: - raise TranslationUnitSaveError(result, - 'Error saving TranslationUnit.') - - def codeComplete(self, path, line, column, unsaved_files=None, - include_macros=False, include_code_patterns=False, - include_brief_comments=False): - """ - Code complete in this translation unit. - - In-memory contents for files can be provided by passing a list of pairs - as unsaved_files, the first items should be the filenames to be mapped - and the second should be the contents to be substituted for the - file. The contents may be passed as strings or file objects. - """ - options = 0 - - if include_macros: - options += 1 - - if include_code_patterns: - options += 2 - - if include_brief_comments: - options += 4 - - if unsaved_files is None: - unsaved_files = [] - - unsaved_files_array = 0 - if len(unsaved_files): - unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() - for i,(name,value) in enumerate(unsaved_files): - if not isinstance(value, str): - # FIXME: It would be great to support an efficient version - # of this, one day. - value = value.read() - print value - if not isinstance(value, str): - raise TypeError,'Unexpected unsaved file contents.' - unsaved_files_array[i].name = name - unsaved_files_array[i].contents = value - unsaved_files_array[i].length = len(value) - ptr = conf.lib.clang_codeCompleteAt(self, path, line, column, - unsaved_files_array, len(unsaved_files), options) - if ptr: - return CodeCompletionResults(ptr) - return None - - def get_tokens(self, locations=None, extent=None): - """Obtain tokens in this translation unit. - - This is a generator for Token instances. The caller specifies a range - of source code to obtain tokens for. The range can be specified as a - 2-tuple of SourceLocation or as a SourceRange. If both are defined, - behavior is undefined. - """ - if locations is not None: - extent = SourceRange(start=locations[0], end=locations[1]) - - return TokenGroup.get_tokens(self, extent) + def get_includes(self): + """ + Return an iterable sequence of FileInclusion objects that describe the + sequence of inclusions in a translation unit. The first object in + this sequence is always the input file. Note that this method will not + recursively iterate over header files included through precompiled + headers. + """ + def visitor(fobj, lptr, depth, includes): + if depth > 0: + loc = lptr.contents + includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) + + # Automatically adapt CIndex/ctype pointers to python objects + includes = [] + conf.lib.clang_getInclusions(self, + callbacks['translation_unit_includes'](visitor), includes) + + return iter(includes) + + def get_file(self, filename): + """Obtain a File from this translation unit.""" + + return File.from_name(self, filename) + + def get_location(self, filename, position): + """Obtain a SourceLocation for a file in this translation unit. + + The position can be specified by passing: + + - Integer file offset. Initial file offset is 0. + - 2-tuple of (line number, column number). Initial file position is + (0, 0) + """ + f = self.get_file(filename) + + if isinstance(position, int): + return SourceLocation.from_offset(self, f, position) + + return SourceLocation.from_position(self, f, position[0], position[1]) + + def get_extent(self, filename, locations): + """Obtain a SourceRange from this translation unit. + + The bounds of the SourceRange must ultimately be defined by a start and + end SourceLocation. For the locations argument, you can pass: + + - 2 SourceLocation instances in a 2-tuple or list. + - 2 int file offsets via a 2-tuple or list. + - 2 2-tuple or lists of (line, column) pairs in a 2-tuple or list. + + e.g. + + get_extent('foo.c', (5, 10)) + get_extent('foo.c', ((1, 1), (1, 15))) + """ + f = self.get_file(filename) + + if len(locations) < 2: + raise Exception('Must pass object with at least 2 elements') + + start_location, end_location = locations + + if hasattr(start_location, '__len__'): + start_location = SourceLocation.from_position(self, f, + start_location[0], start_location[1]) + elif isinstance(start_location, int): + start_location = SourceLocation.from_offset(self, f, + start_location) + + if hasattr(end_location, '__len__'): + end_location = SourceLocation.from_position(self, f, + end_location[0], end_location[1]) + elif isinstance(end_location, int): + end_location = SourceLocation.from_offset(self, f, end_location) + else: + end_location = SourceLocation.from_offset(self, f, int(end_location)) + + + assert isinstance(start_location, SourceLocation) + assert isinstance(end_location, SourceLocation) + + return SourceRange.from_locations(start_location, end_location) + + @property + def diagnostics(self): + """ + Return an iterable (and indexable) object containing the diagnostics. + """ + class DiagIterator: + def __init__(self, tu): + self.tu = tu + + def __len__(self): + return int(conf.lib.clang_getNumDiagnostics(self.tu)) + + def __getitem__(self, key): + diag = conf.lib.clang_getDiagnostic(self.tu, key) + if not diag: + raise IndexError + return Diagnostic(diag) + + return DiagIterator(self) + + def reparse(self, unsaved_files=None, options=0): + """ + Reparse an already parsed translation unit. + + In-memory contents for files can be provided by passing a list of pairs + as unsaved_files, the first items should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + if unsaved_files is None: + unsaved_files = [] + + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print value + if not isinstance(value, str): + raise TypeError,'Unexpected unsaved file contents.' + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = conf.lib.clang_reparseTranslationUnit(self, len(unsaved_files), + unsaved_files_array, options) + + def save(self, filename): + """Saves the TranslationUnit to a file. + + This is equivalent to passing -emit-ast to the clang frontend. The + saved file can be loaded back into a TranslationUnit. Or, if it + corresponds to a header, it can be used as a pre-compiled header file. + + If an error occurs while saving, a TranslationUnitSaveError is raised. + If the error was TranslationUnitSaveError.ERROR_INVALID_TU, this means + the constructed TranslationUnit was not valid at time of save. In this + case, the reason(s) why should be available via + TranslationUnit.diagnostics(). + + filename -- The path to save the translation unit to. + """ + options = conf.lib.clang_defaultSaveOptions(self) + result = int(conf.lib.clang_saveTranslationUnit(self, filename, + options)) + if result != 0: + raise TranslationUnitSaveError(result, + 'Error saving TranslationUnit.') + + def codeComplete(self, path, line, column, unsaved_files=None, + include_macros=False, include_code_patterns=False, + include_brief_comments=False): + """ + Code complete in this translation unit. + + In-memory contents for files can be provided by passing a list of pairs + as unsaved_files, the first items should be the filenames to be mapped + and the second should be the contents to be substituted for the + file. The contents may be passed as strings or file objects. + """ + options = 0 + + if include_macros: + options += 1 + + if include_code_patterns: + options += 2 + + if include_brief_comments: + options += 4 + + if unsaved_files is None: + unsaved_files = [] + + unsaved_files_array = 0 + if len(unsaved_files): + unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() + for i,(name,value) in enumerate(unsaved_files): + if not isinstance(value, str): + # FIXME: It would be great to support an efficient version + # of this, one day. + value = value.read() + print value + if not isinstance(value, str): + raise TypeError,'Unexpected unsaved file contents.' + unsaved_files_array[i].name = name + unsaved_files_array[i].contents = value + unsaved_files_array[i].length = len(value) + ptr = conf.lib.clang_codeCompleteAt(self, path, line, column, + unsaved_files_array, len(unsaved_files), options) + if ptr: + return CodeCompletionResults(ptr) + return None + + def get_tokens(self, locations=None, extent=None): + """Obtain tokens in this translation unit. + + This is a generator for Token instances. The caller specifies a range + of source code to obtain tokens for. The range can be specified as a + 2-tuple of SourceLocation or as a SourceRange. If both are defined, + behavior is undefined. + """ + if locations is not None: + extent = SourceRange(start=locations[0], end=locations[1]) + + return TokenGroup.get_tokens(self, extent) class File(ClangObject): - """ - The File class represents a particular source file that is part of a - translation unit. - """ + """ + The File class represents a particular source file that is part of a + translation unit. + """ - @staticmethod - def from_name(translation_unit, file_name): - """Retrieve a file handle within the given translation unit.""" - return File(conf.lib.clang_getFile(translation_unit, file_name)) + @staticmethod + def from_name(translation_unit, file_name): + """Retrieve a file handle within the given translation unit.""" + return File(conf.lib.clang_getFile(translation_unit, file_name)) - @property - def name(self): - """Return the complete file and path name of the file.""" - return conf.lib.clang_getCString(conf.lib.clang_getFileName(self)) + @property + def name(self): + """Return the complete file and path name of the file.""" + return conf.lib.clang_getCString(conf.lib.clang_getFileName(self)) - @property - def time(self): - """Return the last modification time of the file.""" - return conf.lib.clang_getFileTime(self) + @property + def time(self): + """Return the last modification time of the file.""" + return conf.lib.clang_getFileTime(self) - def __str__(self): - return self.name + def __str__(self): + return self.name - def __repr__(self): - return "" % (self.name) + def __repr__(self): + return "" % (self.name) - @staticmethod - def from_cursor_result(res, fn, args): - assert isinstance(res, File) + @staticmethod + def from_cursor_result(res, fn, args): + assert isinstance(res, File) - # Copy a reference to the TranslationUnit to prevent premature GC. - res._tu = args[0]._tu - return res + # Copy a reference to the TranslationUnit to prevent premature GC. + res._tu = args[0]._tu + return res class FileInclusion(object): - """ - The FileInclusion class represents the inclusion of one source file by - another via a '#include' directive or as the input file for the translation - unit. This class provides information about the included file, the including - file, the location of the '#include' directive and the depth of the included - file in the stack. Note that the input file has depth 0. - """ - - def __init__(self, src, tgt, loc, depth): - self.source = src - self.include = tgt - self.location = loc - self.depth = depth - - @property - def is_input_file(self): - """True if the included file is the input file.""" - return self.depth == 0 + """ + The FileInclusion class represents the inclusion of one source file by + another via a '#include' directive or as the input file for the translation + unit. This class provides information about the included file, the including + file, the location of the '#include' directive and the depth of the included + file in the stack. Note that the input file has depth 0. + """ + + def __init__(self, src, tgt, loc, depth): + self.source = src + self.include = tgt + self.location = loc + self.depth = depth + + @property + def is_input_file(self): + """True if the included file is the input file.""" + return self.depth == 0 class CompilationDatabaseError(Exception): - """Represents an error that occurred when working with a CompilationDatabase + """Represents an error that occurred when working with a CompilationDatabase - Each error is associated to an enumerated value, accessible under - e.cdb_error. Consumers can compare the value with one of the ERROR_ - constants in this class. - """ + Each error is associated to an enumerated value, accessible under + e.cdb_error. Consumers can compare the value with one of the ERROR_ + constants in this class. + """ - # An unknown error occurred - ERROR_UNKNOWN = 0 + # An unknown error occurred + ERROR_UNKNOWN = 0 - # The database could not be loaded - ERROR_CANNOTLOADDATABASE = 1 + # The database could not be loaded + ERROR_CANNOTLOADDATABASE = 1 - def __init__(self, enumeration, message): - assert isinstance(enumeration, int) + def __init__(self, enumeration, message): + assert isinstance(enumeration, int) - if enumeration > 1: - raise Exception("Encountered undefined CompilationDatabase error " - "constant: %d. Please file a bug to have this " - "value supported." % enumeration) + if enumeration > 1: + raise Exception("Encountered undefined CompilationDatabase error " + "constant: %d. Please file a bug to have this " + "value supported." % enumeration) - self.cdb_error = enumeration - Exception.__init__(self, 'Error %d: %s' % (enumeration, message)) + self.cdb_error = enumeration + Exception.__init__(self, 'Error %d: %s' % (enumeration, message)) class CompileCommand(object): - """Represents the compile command used to build a file""" - def __init__(self, cmd, ccmds): - self.cmd = cmd - # Keep a reference to the originating CompileCommands - # to prevent garbage collection - self.ccmds = ccmds - - @property - def directory(self): - """Get the working directory for this CompileCommand""" - return conf.lib.clang_CompileCommand_getDirectory(self.cmd) - - @property - def filename(self): - """Get the working filename for this CompileCommand""" - return conf.lib.clang_CompileCommand_getFilename(self.cmd) - - @property - def arguments(self): - """ - Get an iterable object providing each argument in the - command line for the compiler invocation as a _CXString. - - Invariant : the first argument is the compiler executable - """ - length = conf.lib.clang_CompileCommand_getNumArgs(self.cmd) - for i in xrange(length): - yield conf.lib.clang_CompileCommand_getArg(self.cmd, i) + """Represents the compile command used to build a file""" + def __init__(self, cmd, ccmds): + self.cmd = cmd + # Keep a reference to the originating CompileCommands + # to prevent garbage collection + self.ccmds = ccmds + + @property + def directory(self): + """Get the working directory for this CompileCommand""" + return conf.lib.clang_CompileCommand_getDirectory(self.cmd) + + @property + def filename(self): + """Get the working filename for this CompileCommand""" + return conf.lib.clang_CompileCommand_getFilename(self.cmd) + + @property + def arguments(self): + """ + Get an iterable object providing each argument in the + command line for the compiler invocation as a _CXString. + + Invariant : the first argument is the compiler executable + """ + length = conf.lib.clang_CompileCommand_getNumArgs(self.cmd) + for i in xrange(length): + yield conf.lib.clang_CompileCommand_getArg(self.cmd, i) class CompileCommands(object): - """ - CompileCommands is an iterable object containing all CompileCommand - that can be used for building a specific file. - """ - def __init__(self, ccmds): - self.ccmds = ccmds - - def __del__(self): - conf.lib.clang_CompileCommands_dispose(self.ccmds) - - def __len__(self): - return int(conf.lib.clang_CompileCommands_getSize(self.ccmds)) - - def __getitem__(self, i): - cc = conf.lib.clang_CompileCommands_getCommand(self.ccmds, i) - if not cc: - raise IndexError - return CompileCommand(cc, self) - - @staticmethod - def from_result(res, fn, args): - if not res: - return None - return CompileCommands(res) + """ + CompileCommands is an iterable object containing all CompileCommand + that can be used for building a specific file. + """ + def __init__(self, ccmds): + self.ccmds = ccmds + + def __del__(self): + conf.lib.clang_CompileCommands_dispose(self.ccmds) + + def __len__(self): + return int(conf.lib.clang_CompileCommands_getSize(self.ccmds)) + + def __getitem__(self, i): + cc = conf.lib.clang_CompileCommands_getCommand(self.ccmds, i) + if not cc: + raise IndexError + return CompileCommand(cc, self) + + @staticmethod + def from_result(res, fn, args): + if not res: + return None + return CompileCommands(res) class CompilationDatabase(ClangObject): - """ - The CompilationDatabase is a wrapper class around - clang::tooling::CompilationDatabase - - It enables querying how a specific source file can be built. - """ - - def __del__(self): - conf.lib.clang_CompilationDatabase_dispose(self) - - @staticmethod - def from_result(res, fn, args): - if not res: - raise CompilationDatabaseError(0, - "CompilationDatabase loading failed") - return CompilationDatabase(res) - - @staticmethod - def fromDirectory(buildDir): - """Builds a CompilationDatabase from the database found in buildDir""" - errorCode = c_uint() - try: - cdb = conf.lib.clang_CompilationDatabase_fromDirectory(buildDir, - byref(errorCode)) - except CompilationDatabaseError as e: - raise CompilationDatabaseError(int(errorCode.value), - "CompilationDatabase loading failed") - return cdb - - def getCompileCommands(self, filename): - """ - Get an iterable object providing all the CompileCommands available to - build filename. Returns None if filename is not found in the database. - """ - return conf.lib.clang_CompilationDatabase_getCompileCommands(self, - filename) - - def getAllCompileCommands(self): - """ - Get an iterable object providing all the CompileCommands available from - the database. - """ - return conf.lib.clang_CompilationDatabase_getAllCompileCommands(self) + """ + The CompilationDatabase is a wrapper class around + clang::tooling::CompilationDatabase + + It enables querying how a specific source file can be built. + """ + + def __del__(self): + conf.lib.clang_CompilationDatabase_dispose(self) + + @staticmethod + def from_result(res, fn, args): + if not res: + raise CompilationDatabaseError(0, + "CompilationDatabase loading failed") + return CompilationDatabase(res) + + @staticmethod + def fromDirectory(buildDir): + """Builds a CompilationDatabase from the database found in buildDir""" + errorCode = c_uint() + try: + cdb = conf.lib.clang_CompilationDatabase_fromDirectory(buildDir, + byref(errorCode)) + except CompilationDatabaseError as e: + raise CompilationDatabaseError(int(errorCode.value), + "CompilationDatabase loading failed") + return cdb + + def getCompileCommands(self, filename): + """ + Get an iterable object providing all the CompileCommands available to + build filename. Returns None if filename is not found in the database. + """ + return conf.lib.clang_CompilationDatabase_getCompileCommands(self, + filename) + + def getAllCompileCommands(self): + """ + Get an iterable object providing all the CompileCommands available from + the database. + """ + return conf.lib.clang_CompilationDatabase_getAllCompileCommands(self) class Token(Structure): - """Represents a single token from the preprocessor. + """Represents a single token from the preprocessor. - Tokens are effectively segments of source code. Source code is first parsed - into tokens before being converted into the AST and Cursors. + Tokens are effectively segments of source code. Source code is first parsed + into tokens before being converted into the AST and Cursors. - Tokens are obtained from parsed TranslationUnit instances. You currently - can't create tokens manually. - """ - _fields_ = [ - ('int_data', c_uint * 4), - ('ptr_data', c_void_p) - ] + Tokens are obtained from parsed TranslationUnit instances. You currently + can't create tokens manually. + """ + _fields_ = [ + ('int_data', c_uint * 4), + ('ptr_data', c_void_p) + ] - @property - def spelling(self): - """The spelling of this token. + @property + def spelling(self): + """The spelling of this token. - This is the textual representation of the token in source. - """ - return conf.lib.clang_getTokenSpelling(self._tu, self) + This is the textual representation of the token in source. + """ + return conf.lib.clang_getTokenSpelling(self._tu, self) - @property - def kind(self): - """Obtain the TokenKind of the current token.""" - return TokenKind.from_value(conf.lib.clang_getTokenKind(self)) + @property + def kind(self): + """Obtain the TokenKind of the current token.""" + return TokenKind.from_value(conf.lib.clang_getTokenKind(self)) - @property - def location(self): - """The SourceLocation this Token occurs at.""" - return conf.lib.clang_getTokenLocation(self._tu, self) + @property + def location(self): + """The SourceLocation this Token occurs at.""" + return conf.lib.clang_getTokenLocation(self._tu, self) - @property - def extent(self): - """The SourceRange this Token occupies.""" - return conf.lib.clang_getTokenExtent(self._tu, self) + @property + def extent(self): + """The SourceRange this Token occupies.""" + return conf.lib.clang_getTokenExtent(self._tu, self) - @property - def cursor(self): - """The Cursor this Token corresponds to.""" - cursor = Cursor() + @property + def cursor(self): + """The Cursor this Token corresponds to.""" + cursor = Cursor() - conf.lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor)) + conf.lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor)) - return cursor + return cursor # Now comes the plumbing to hook up the C library. # Register callback types in common container. callbacks['translation_unit_includes'] = CFUNCTYPE(None, c_object_p, - POINTER(SourceLocation), c_uint, py_object) + POINTER(SourceLocation), c_uint, py_object) callbacks['cursor_visit'] = CFUNCTYPE(c_int, Cursor, Cursor, py_object) callbacks['fields_visit'] = CFUNCTYPE(c_int, Cursor, py_object) @@ -3348,7 +3348,7 @@ def cursor(self): ("clang_getInstantiationLocation", [SourceLocation, POINTER(c_object_p), POINTER(c_uint), POINTER(c_uint), - POINTER(c_uint)]), + POINTER(c_uint)]), ("clang_getLocation", [TranslationUnit, File, c_uint, c_uint], @@ -3648,170 +3648,171 @@ def cursor(self): ] class LibclangError(Exception): - def __init__(self, message): - self.m = message + def __init__(self, message): + self.m = message - def __str__(self): - return self.m + def __str__(self): + return self.m def register_function(lib, item, ignore_errors): - # A function may not exist, if these bindings are used with an older or - # incompatible version of libclang.so. - try: - func = getattr(lib, item[0]) - except AttributeError as e: - msg = str(e) + ". Please ensure that your python bindings are "\ - "compatible with your libclang.so version." - if ignore_errors: - return - raise LibclangError(msg) - - if len(item) >= 2: - func.argtypes = item[1] - - if len(item) >= 3: - func.restype = item[2] - - if len(item) == 4: - func.errcheck = item[3] + # A function may not exist, if these bindings are used with an older or + # incompatible version of libclang.so. + try: + func = getattr(lib, item[0]) + except AttributeError as e: + msg = str(e) + ". Please ensure that your python bindings are "\ + "compatible with your libclang.so version." + if ignore_errors: + return + raise LibclangError(msg) + + if len(item) >= 2: + func.argtypes = item[1] + + if len(item) >= 3: + func.restype = item[2] + + if len(item) == 4: + func.errcheck = item[3] def register_functions(lib, ignore_errors): - """Register function prototypes with a libclang library instance. + """Register function prototypes with a libclang library instance. - This must be called as part of library instantiation so Python knows how - to call out to the shared library. - """ + This must be called as part of library instantiation so Python knows how + to call out to the shared library. + """ - def register(item): - return register_function(lib, item, ignore_errors) + def register(item): + return register_function(lib, item, ignore_errors) - map(register, functionList) + map(register, functionList) class Config: - library_path = None - library_file = None - compatibility_check = True - loaded = False - _lib=None - - @staticmethod - def set_library_path(path): - """Set the path in which to search for libclang""" - if Config.loaded: - raise Exception("library path must be set before before using " \ - "any other functionalities in libclang.") - - Config.library_path = path - - @staticmethod - def set_library_file(filename): - """Set the exact location of libclang""" - if Config.loaded: - raise Exception("library file must be set before before using " \ - "any other functionalities in libclang.") - - Config.library_file = filename - - @staticmethod - def set_compatibility_check(check_status): - """ Perform compatibility check when loading libclang - - The python bindings are only tested and evaluated with the version of - libclang they are provided with. To ensure correct behavior a (limited) - compatibility check is performed when loading the bindings. This check - will throw an exception, as soon as it fails. - - In case these bindings are used with an older version of libclang, parts - that have been stable between releases may still work. Users of the - python bindings can disable the compatibility check. This will cause - the python bindings to load, even though they are written for a newer - version of libclang. Failures now arise if unsupported or incompatible - features are accessed. The user is required to test themselves if the - features they are using are available and compatible between different - libclang versions. - """ - if Config.loaded: - raise Exception("compatibility_check must be set before before " \ - "using any other functionalities in libclang.") - - Config.compatibility_check = check_status - - def unload_lib(self): - _lib=None - - # was @CachedProperty but we need to reload in case of a crash. - @property - def lib(self): - if self._lib: - return self._lib - self._lib = self.get_cindex_library() - register_functions(self._lib, not Config.compatibility_check) - Config.loaded = True - return self._lib - - def get_filename(self): - if Config.library_file: - return Config.library_file - - import platform - name = platform.system() - - if name == 'Darwin': - file = 'libclang.dylib' - elif name == 'Windows': - file = 'libclang.dll' - else: - file = 'libclang.so' - - if Config.library_path: - file = Config.library_path + '/' + file - - return file - - def get_cindex_library(self): - try: - library = cdll.LoadLibrary(self.get_filename()) - except OSError as e: - msg = str(e) + ". To provide a path to libclang use " \ - "Config.set_library_path() or " \ - "Config.set_library_file()." - raise LibclangError(msg) - - return library - - def function_exists(self, name): - try: - getattr(self.lib, name) - except AttributeError: - return False - - return True + library_path = None + library_file = None + compatibility_check = True + loaded = False + _lib=None + + @staticmethod + def set_library_path(path): + """Set the path in which to search for libclang""" + if Config.loaded: + raise Exception("library path must be set before before using " \ + "any other functionalities in libclang.") + + Config.library_path = path + + @staticmethod + def set_library_file(filename): + """Set the exact location of libclang""" + if Config.loaded: + raise Exception("library file must be set before before using " \ + "any other functionalities in libclang.") + + Config.library_file = filename + + @staticmethod + def set_compatibility_check(check_status): + """ Perform compatibility check when loading libclang + + The python bindings are only tested and evaluated with the version of + libclang they are provided with. To ensure correct behavior a (limited) + compatibility check is performed when loading the bindings. This check + will throw an exception, as soon as it fails. + + In case these bindings are used with an older version of libclang, parts + that have been stable between releases may still work. Users of the + python bindings can disable the compatibility check. This will cause + the python bindings to load, even though they are written for a newer + version of libclang. Failures now arise if unsupported or incompatible + features are accessed. The user is required to test themselves if the + features they are using are available and compatible between different + libclang versions. + """ + if Config.loaded: + raise Exception("compatibility_check must be set before before " \ + "using any other functionalities in libclang.") + + Config.compatibility_check = check_status + + def unload_lib(self): + _lib=None + + # was @CachedProperty but we need to reload in case of a crash. + @property + def lib(self): + if self._lib: + return self._lib + self._lib = self.get_cindex_library() + register_functions(self._lib, not Config.compatibility_check) + Config.loaded = True + return self._lib + + def get_filename(self): + if Config.library_file: + return Config.library_file + + import platform + name = platform.system() + + if name == 'Darwin': + file = 'libclang.dylib' + elif name == 'Windows': + file = 'libclang.dll' + else: + file = 'libclang.so' + + if Config.library_path: + file = Config.library_path + '/' + file + + return file + + def get_cindex_library(self): + try: + fn=self.get_filename() + library = cdll.LoadLibrary(fn) + except OSError as e: + msg = str(e) + ". To provide a path to libclang use " \ + "Config.set_library_path() or " \ + "Config.set_library_file()." + raise LibclangError(msg) + + return library + + def function_exists(self, name): + try: + getattr(self.lib, name) + except AttributeError: + return False + + return True def register_enumerations(): - for name, value in enumerations.TokenKinds: - TokenKind.register(value, name) + for name, value in enumerations.TokenKinds: + TokenKind.register(value, name) conf = Config() register_enumerations() __all__ = [ - 'Config', - 'CodeCompletionResults', - 'CompilationDatabase', - 'CompileCommands', - 'CompileCommand', - 'CursorKind', - 'Cursor', - 'Diagnostic', - 'File', - 'FixIt', - 'Index', - 'SourceLocation', - 'SourceRange', - 'TokenKind', - 'Token', - 'TranslationUnitLoadError', - 'TranslationUnit', - 'TypeKind', - 'Type', + 'Config', + 'CodeCompletionResults', + 'CompilationDatabase', + 'CompileCommands', + 'CompileCommand', + 'CursorKind', + 'Cursor', + 'Diagnostic', + 'File', + 'FixIt', + 'Index', + 'SourceLocation', + 'SourceRange', + 'TokenKind', + 'Token', + 'TranslationUnitLoadError', + 'TranslationUnit', + 'TypeKind', + 'Type', ] diff --git a/cldoc/clang/enumerations.py b/cldoc/clang/enumerations.py index a86a48a..03aad0b 100644 --- a/cldoc/clang/enumerations.py +++ b/cldoc/clang/enumerations.py @@ -1,6 +1,6 @@ #===- enumerations.py - Python Enumerations ------------------*- python -*--===# # -# The LLVM Compiler Infrastructure +# The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. @@ -24,11 +24,11 @@ # Maps to CXTokenKind. Note that libclang maintains a separate set of token # enumerations from the C++ API. TokenKinds = [ - ('PUNCTUATION', 0), - ('KEYWORD', 1), - ('IDENTIFIER', 2), - ('LITERAL', 3), - ('COMMENT', 4), + ('PUNCTUATION', 0), + ('KEYWORD', 1), + ('IDENTIFIER', 2), + ('LITERAL', 3), + ('COMMENT', 4), ] __all__ = ['TokenKinds'] diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index b19b36a..41bd942 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -19,150 +19,150 @@ import glob def run_generate(t, opts): - if opts.type != 'html' and opts.type != 'xml' and opts.type != 'md': - return + if opts.type != 'html' and opts.type != 'xml' and opts.type != 'md': + return - from . import generators + from . import generators - if opts.type == 'html' and opts.static: - baseout = fs.fs.mkdtemp() - else: - baseout = opts.output + if opts.type == 'html' and opts.static: + baseout = fs.fs.mkdtemp() + else: + baseout = opts.output - xmlout = os.path.join(baseout, 'xml') - if opts.type == 'xml': - generator = generators.Xml(t, opts) - generator.generate('C:\\Simul\\master\\Simul\\Help\\docout\\xml') - if opts.type == 'md': - generator_md = generators.Md(t, opts) - generator_md.generate(baseout) + xmlout = os.path.join(baseout, 'xml') + if opts.type == 'xml': + generator = generators.Xml(t, opts) + generator.generate('C:\\Simul\\master\\Simul\\Help\\docout\\xml') + if opts.type == 'md': + generator_md = generators.Md(t, opts) + generator_md.generate(baseout) - if opts.type == 'html': - generators.Html(t).generate(baseout, opts.static, opts.custom_js, opts.custom_css) + if opts.type == 'html': + generators.Html(t).generate(baseout, opts.static, opts.custom_js, opts.custom_css) - if opts.static: - staticsite.generate(baseout, opts) - if opts.post != '': - args=opts.post.split(' ') - subprocess.call(args,shell=True) + if opts.static: + staticsite.generate(baseout, opts) + if opts.post != '': + args=opts.post.split(' ') + subprocess.call(args,shell=True) def run(args): - try: - sep = args.index('--') - except ValueError: - if not '--help' in args: - sys.stderr.write('Please use: cldoc generate [CXXFLAGS] -- [OPTIONS] [FILES]\n') - sys.exit(1) - else: - sep = -1 - - parser = argparse.ArgumentParser(description='clang based documentation generator.', - usage='%(prog)s generate [CXXFLAGS] -- [OPTIONS] [FILES]') - - parser.add_argument('--quiet', default=False, action='store_const', const=True, - help='be quiet about it') + try: + sep = args.index('--') + except ValueError: + if not '--help' in args: + sys.stderr.write('Please use: cldoc generate [CXXFLAGS] -- [OPTIONS] [FILES]\n') + sys.exit(1) + else: + sep = -1 + + parser = argparse.ArgumentParser(description='clang based documentation generator.', + usage='%(prog)s generate [CXXFLAGS] -- [OPTIONS] [FILES]') + + parser.add_argument('--quiet', default=False, action='store_const', const=True, + help='be quiet about it') - parser.add_argument('--loglevel', default='error', metavar='LEVEL', - help='specify the logevel (error, warning, info)') + parser.add_argument('--loglevel', default='error', metavar='LEVEL', + help='specify the logevel (error, warning, info)') - parser.add_argument('--report', default=False, - action='store_const', const=True, help='report documentation coverage and errors') + parser.add_argument('--report', default=False, + action='store_const', const=True, help='report documentation coverage and errors') - parser.add_argument('--output', default=None, metavar='DIR', - help='specify the output directory') + parser.add_argument('--output', default=None, metavar='DIR', + help='specify the output directory') - parser.add_argument('--md_output', default='', metavar='DIR', - help='specify the relative markdown generated files output directory') - - parser.add_argument('--language', default='c++', metavar='LANGUAGE', - help='specify the default parse language (c++, c or objc)') + parser.add_argument('--md_output', default='', metavar='DIR', + help='specify the relative markdown generated files output directory') + + parser.add_argument('--language', default='c++', metavar='LANGUAGE', + help='specify the default parse language (c++, c or objc)') - parser.add_argument('--type', default='html', metavar='TYPE', - help='specify the type of output (html or xml, default html)') + parser.add_argument('--type', default='html', metavar='TYPE', + help='specify the type of output (html or xml, default html)') - parser.add_argument('--merge', default=[], metavar='FILES', action='append', - help='specify additional description files to merge into the documentation') + parser.add_argument('--merge', default=[], metavar='FILES', action='append', + help='specify additional description files to merge into the documentation') - parser.add_argument('--merge-filter', default=None, metavar='FILTER', - help='specify program to pass merged description files through') + parser.add_argument('--merge-filter', default=None, metavar='FILTER', + help='specify program to pass merged description files through') - parser.add_argument('--basedir', default=None, metavar='DIR', - help='the project base directory') + parser.add_argument('--basedir', default=None, metavar='DIR', + help='the project base directory') - parser.add_argument('--static', default=False, action='store_const', const=True, - help='generate a static website (only for when --output is html, requires globally installed cldoc-static via npm)') + parser.add_argument('--static', default=False, action='store_const', const=True, + help='generate a static website (only for when --output is html, requires globally installed cldoc-static via npm)') - parser.add_argument('--custom-js', default=[], metavar='FILES', action='append', - help='specify additional javascript files to be merged into the html (only for when --output is html)') + parser.add_argument('--custom-js', default=[], metavar='FILES', action='append', + help='specify additional javascript files to be merged into the html (only for when --output is html)') - parser.add_argument('--custom-css', default=[], metavar='FILES', action='append', - help='specify additional css files to be merged into the html (only for when --output is html)') - - parser.add_argument('--clean', default=None, metavar='CLEAN', - help='directory to clean before running') + parser.add_argument('--custom-css', default=[], metavar='FILES', action='append', + help='specify additional css files to be merged into the html (only for when --output is html)') + + parser.add_argument('--clean', default=None, metavar='CLEAN', + help='directory to clean before running') - parser.add_argument('--strip', default=None, metavar='STRIP', - help='path to remove from filenames') + parser.add_argument('--strip', default=None, metavar='STRIP', + help='path to remove from filenames') - parser.add_argument('--post', default=None, metavar='POST', - help='command to execute after completion') + parser.add_argument('--post', default=None, metavar='POST', + help='command to execute after completion') - parser.add_argument('files', nargs='+', help='files to parse') + parser.add_argument('files', nargs='+', help='files to parse') - restargs = args[sep + 1:] - cxxflags = args[:sep] + restargs = args[sep + 1:] + cxxflags = args[:sep] - opts = parser.parse_args(restargs) - newfiles=[] - for filepath in opts.files: - gfiles=glob.glob(filepath) - newfiles=newfiles+gfiles - opts.files=newfiles - if opts.quiet: - sys.stdout = open(os.devnull, 'w') - if opts.clean: - r = glob.glob(opts.clean+'/*') - for i in r: - if os.path.isdir(i): - shutil.rmtree(i) - else: - os.remove(i) + opts = parser.parse_args(restargs) + newfiles=[] + for filepath in opts.files: + gfiles=glob.glob(filepath) + newfiles=newfiles+gfiles + opts.files=newfiles + if opts.quiet: + sys.stdout = open(os.devnull, 'w') + if opts.clean: + r = glob.glob(opts.clean+'/*') + for i in r: + if os.path.isdir(i): + shutil.rmtree(i) + else: + os.remove(i) - log.setLevel(opts.loglevel) + log.setLevel(opts.loglevel) - from . import tree - - if opts.strip: - opts.strip=opts.strip.replace('\\','/') - opts.strip=opts.strip.replace('//','/') - - if not opts.output: - sys.stderr.write("Please specify the output directory\n") - sys.exit(1) + from . import tree + + if opts.strip: + opts.strip=opts.strip.replace('\\','/') + opts.strip=opts.strip.replace('//','/') + + if not opts.output: + sys.stderr.write("Please specify the output directory\n") + sys.exit(1) - if opts.static and opts.type != 'html': - sys.stderr.write("The --static option can only be used with the html output format\n") - sys.exit(1) + if opts.static and opts.type != 'html': + sys.stderr.write("The --static option can only be used with the html output format\n") + sys.exit(1) - haslang = False + haslang = False - for x in cxxflags: - if x.startswith('-x'): - haslang = True + for x in cxxflags: + if x.startswith('-x'): + haslang = True - if not haslang: - cxxflags.append('-x') - cxxflags.append(opts.language) + if not haslang: + cxxflags.append('-x') + cxxflags.append(opts.language) - t = tree.Tree(opts.files, cxxflags, opts) + t = tree.Tree(opts.files, cxxflags, opts) - t.process() + t.process() - if opts.merge: - t.merge(opts.merge_filter, opts.merge) + if opts.merge: + t.merge(opts.merge_filter, opts.merge) - t.cross_ref() + t.cross_ref() - run_generate(t, opts) + run_generate(t, opts) # vi:ts=4:et diff --git a/cldoc/cmdgir.py b/cldoc/cmdgir.py index d39044c..a51aa69 100644 --- a/cldoc/cmdgir.py +++ b/cldoc/cmdgir.py @@ -15,9 +15,9 @@ import sys, argparse, re, os try: - from xml.etree import cElementTree as ElementTree + from xml.etree import cElementTree as ElementTree except: - from xml.etree import ElementTree + from xml.etree import ElementTree from cldoc.clang import cindex @@ -31,922 +31,922 @@ from . import utf8 def nsgtk(s): - return '{{{0}}}{1}'.format('http://www.gtk.org/introspection/core/1.0', s) + return '{{{0}}}{1}'.format('http://www.gtk.org/introspection/core/1.0', s) def nsc(s): - return '{{{0}}}{1}'.format('http://www.gtk.org/introspection/c/1.0', s) + return '{{{0}}}{1}'.format('http://www.gtk.org/introspection/c/1.0', s) def nsglib(s): - return '{{{0}}}{1}'.format('http://www.gtk.org/introspection/glib/1.0', s) + return '{{{0}}}{1}'.format('http://www.gtk.org/introspection/glib/1.0', s) def stripns(tag): - try: - pos = tag.index('}') - return tag[pos+1:] - except: - return tag + try: + pos = tag.index('}') + return tag[pos+1:] + except: + return tag class Interface(nodes.Class): - @property - def classname(self): - return '{http://jessevdk.github.com/cldoc/gobject/1.0}interface' + @property + def classname(self): + return '{http://jessevdk.github.com/cldoc/gobject/1.0}interface' class Class(nodes.Class): - def __init__(self, cursor, comment): - nodes.Class.__init__(self, cursor, comment) + def __init__(self, cursor, comment): + nodes.Class.__init__(self, cursor, comment) - # Extract bases - for b in cursor.bases: - self.bases.append(nodes.Class.Base(b)) + # Extract bases + for b in cursor.bases: + self.bases.append(nodes.Class.Base(b)) - for i in cursor.implements: - self.implements.append(nodes.Class.Base(i)) + for i in cursor.implements: + self.implements.append(nodes.Class.Base(i)) - @property - def classname(self): - return '{http://jessevdk.github.com/cldoc/gobject/1.0}class' + @property + def classname(self): + return '{http://jessevdk.github.com/cldoc/gobject/1.0}class' class Property(nodes.Node): - def __init__(self, cursor, comment): - nodes.Node.__init__(self, cursor, comment) + def __init__(self, cursor, comment): + nodes.Node.__init__(self, cursor, comment) - self.type = nodes.Type(cursor.type) + self.type = nodes.Type(cursor.type) - @property - def classname(self): - return '{http://jessevdk.github.com/cldoc/gobject/1.0}property' + @property + def classname(self): + return '{http://jessevdk.github.com/cldoc/gobject/1.0}property' - @property - def props(self): - ret = nodes.Node.props.fget(self) + @property + def props(self): + ret = nodes.Node.props.fget(self) - mode = [] + mode = [] - if not ('writable' in self.cursor.node.attrib and self.cursor.node.attrib['writable'] == '1'): - mode.append('readonly') + if not ('writable' in self.cursor.node.attrib and self.cursor.node.attrib['writable'] == '1'): + mode.append('readonly') - if 'construct-only' in self.cursor.node.attrib and self.cursor.node.attrib['construct-only'] == '1': - mode.append('construct-only') + if 'construct-only' in self.cursor.node.attrib and self.cursor.node.attrib['construct-only'] == '1': + mode.append('construct-only') - if 'construct' in self.cursor.node.attrib and self.cursor.node.attrib['construct'] == '1': - mode.append('construct') + if 'construct' in self.cursor.node.attrib and self.cursor.node.attrib['construct'] == '1': + mode.append('construct') - if len(mode) > 0: - ret['mode'] = ",".join(mode) + if len(mode) > 0: + ret['mode'] = ",".join(mode) - return ret + return ret class Boxed(nodes.Struct): - def __init__(self, cursor, comment): - nodes.Struct.__init__(self, cursor, comment) + def __init__(self, cursor, comment): + nodes.Struct.__init__(self, cursor, comment) - @property - def classname(self): - return '{http://jessevdk.github.com/cldoc/gobject/1.0}boxed' + @property + def classname(self): + return '{http://jessevdk.github.com/cldoc/gobject/1.0}boxed' - @property - def force_page(self): - return True + @property + def force_page(self): + return True class GirComment(comment.Comment): - hashref = re.compile('#([a-z_][a-z0-9_]*)', re.I) - emph = re.compile('(.*?)', re.I) - title = re.compile('(.*?)', re.I) - refsect2 = re.compile('(]*>|)\n?', re.I) - varref = re.compile('@([a-z][a-z0-9_]*)', re.I) - constref = re.compile('%([a-z_][a-z0-9_]*)', re.I) - proglisting = re.compile('\s*\s*(.*?)\s*\s*', re.I | re.M) + hashref = re.compile('#([a-z_][a-z0-9_]*)', re.I) + emph = re.compile('(.*?)', re.I) + title = re.compile('(.*?)', re.I) + refsect2 = re.compile('(]*>|)\n?', re.I) + varref = re.compile('@([a-z][a-z0-9_]*)', re.I) + constref = re.compile('%([a-z_][a-z0-9_]*)', re.I) + proglisting = re.compile('\s*\s*(.*?)\s*\s*', re.I | re.M) - def __init__(self, cursor): - doc = cursor.node.find(nsgtk('doc')) + def __init__(self, cursor): + doc = cursor.node.find(nsgtk('doc')) - if not doc is None: - text = doc.text - else: - text = '' + if not doc is None: + text = doc.text + else: + text = '' - text = self.subst_format(text) + text = self.subst_format(text) - brieftext = text - doctext = '' + brieftext = text + doctext = '' - try: - firstdot = text.index('.') + try: + firstdot = text.index('.') - try: - firstnl = text.index("\n") - except: - firstnl = firstdot + try: + firstnl = text.index("\n") + except: + firstnl = firstdot - if firstnl < firstdot: - firstdot = firstnl - 1 + if firstnl < firstdot: + firstdot = firstnl - 1 - nextnonsp = firstdot + 1 + nextnonsp = firstdot + 1 - while nextnonsp < len(text) and text[nextnonsp] != '\n' and not text[nextnonsp].isspace(): - nextnonsp += 1 + while nextnonsp < len(text) and text[nextnonsp] != '\n' and not text[nextnonsp].isspace(): + nextnonsp += 1 - if nextnonsp != len(text): - # Replicate brief and non brief... - # Insert newline just after . - brieftext = text[:firstdot] - doctext = text - except: - pass + if nextnonsp != len(text): + # Replicate brief and non brief... + # Insert newline just after . + brieftext = text[:firstdot] + doctext = text + except: + pass - if cursor.typename in ['method', 'function', 'virtual-method', 'constructor']: - # Assemble function argument comments and return value comment - preat = [] - postat = [] + if cursor.typename in ['method', 'function', 'virtual-method', 'constructor']: + # Assemble function argument comments and return value comment + preat = [] + postat = [] - for param in cursor.children: - paramdoc = param.node.find(nsgtk('doc')) + for param in cursor.children: + paramdoc = param.node.find(nsgtk('doc')) - if not paramdoc is None: - paramdoc = self.subst_format(paramdoc.text) - else: - paramdoc = '*documentation missing...*' + if not paramdoc is None: + paramdoc = self.subst_format(paramdoc.text) + else: + paramdoc = '*documentation missing...*' - preat.append('@{0} {1}'.format(param.spelling, paramdoc.replace('\n', ' '))) + preat.append('@{0} {1}'.format(param.spelling, paramdoc.replace('\n', ' '))) - return_node = cursor.node.find(nsgtk('return-value')) + return_node = cursor.node.find(nsgtk('return-value')) - if not return_node is None and cursor.type.get_result().spelling != 'void': - doc = return_node.find(nsgtk('doc')) + if not return_node is None and cursor.type.get_result().spelling != 'void': + doc = return_node.find(nsgtk('doc')) - if not doc is None: - postat.append('@return {0}'.format(self.subst_format(doc.text).replace('\n', ' '))) - else: - postat.append('@return *documentation missing...*') + if not doc is None: + postat.append('@return {0}'.format(self.subst_format(doc.text).replace('\n', ' '))) + else: + postat.append('@return *documentation missing...*') - if len(cursor.children) > 0: - preat.append('') + if len(cursor.children) > 0: + preat.append('') - if len(doctext) > 0: - preat.append('') + if len(doctext) > 0: + preat.append('') - if brieftext == '': - brieftext = '*documentation missing...*' + if brieftext == '': + brieftext = '*documentation missing...*' - text = brieftext.replace('\n', ' ').rstrip() + "\n" + "\n".join(preat) + doctext + text = brieftext.replace('\n', ' ').rstrip() + "\n" + "\n".join(preat) + doctext - if len(postat) != 0: - text += '\n\n' + '\n'.join(postat) - else: - if doctext != '': - text = brieftext + "\n\n" + doctext - else: - text = brieftext.replace("\n", ' ') + if len(postat) != 0: + text += '\n\n' + '\n'.join(postat) + else: + if doctext != '': + text = brieftext + "\n\n" + doctext + else: + text = brieftext.replace("\n", ' ') - comment.Comment.__init__(self, text, None) + comment.Comment.__init__(self, text, None) - def subst_format(self, text): - text = GirComment.hashref.sub(lambda x: '<{0}>'.format(x.group(1)), text) - text = GirComment.varref.sub(lambda x: '<{0}>'.format(x.group(1)), text) - text = GirComment.constref.sub(lambda x: '`{0}`'.format(x.group(1)), text) - text = GirComment.emph.sub(lambda x: '*{0}*'.format(x.group(1)), text) - text = GirComment.title.sub(lambda x: '## {0}'.format(x.group(1)), text) - text = GirComment.refsect2.sub(lambda x: '', text) - text = GirComment.proglisting.sub(lambda x: ' [code]\n {0}\n'.format(x.group(1).replace('\n', '\n ')), text) + def subst_format(self, text): + text = GirComment.hashref.sub(lambda x: '<{0}>'.format(x.group(1)), text) + text = GirComment.varref.sub(lambda x: '<{0}>'.format(x.group(1)), text) + text = GirComment.constref.sub(lambda x: '`{0}`'.format(x.group(1)), text) + text = GirComment.emph.sub(lambda x: '*{0}*'.format(x.group(1)), text) + text = GirComment.title.sub(lambda x: '## {0}'.format(x.group(1)), text) + text = GirComment.refsect2.sub(lambda x: '', text) + text = GirComment.proglisting.sub(lambda x: ' [code]\n {0}\n'.format(x.group(1).replace('\n', '\n ')), text) - return text + return text class GirType: - builtins = [ - 'utf8', - 'gchar', - 'gint', - 'gint8', - 'gint16', - 'gint32', - 'gint64', - 'guint', - 'guint8', - 'guint16', - 'guint32', - 'guint64', - 'gfloat', - 'gdouble', - 'gpointer', - 'gsize', - 'gboolean', - 'none' - ]; - - def __init__(self, node): - self.node = node - self.kind = cindex.TypeKind.UNEXPOSED - self.const_qualified = False - - self.is_out = False - self.transfer_ownership = 'none' - self.allow_none = False - - aname = nsc('type') - - if aname in self.node.attrib: - self.spelling = self.node.attrib[aname] - else: - self.spelling = '' - - self._extract_const() - self._extract_kind() - self.declaration = None - - retval = self.node.find(nsgtk('return-value')) - - if not retval is None: - self.return_type = GirCursor(retval).type - - if 'transfer-ownership' in retval.attrib: - self.return_type.transfer_ownership = retval.attrib['transfer-ownership'] - - if 'allow-none' in retval.attrib: - self.return_type.allow_none = retval.attrib['allow-none'] == '1' - else: - self.return_type = None - - def is_builtin(self): - return self.spelling in GirType.builtins - - def _extract_const(self): - prefix = 'const ' - - if self.spelling.startswith(prefix): - self.const_qualified = True - self.spelling = self.spelling[len(prefix):] - - def _extract_kind(self): - if self.spelling == '': - return - - if self.spelling.endswith('*'): - self.kind = cindex.TypeKind.POINTER - return - - for k in nodes.Type.namemap: - if nodes.Type.namemap[k] == self.spelling: - self.kind = k - break - - def get_pointee(self): - return GirTypePointer(self) - - def get_result(self): - return self.return_type - - def get_canonical(self): - return self - - def get_declaration(self): - return self.declaration - - def is_const_qualified(self): - return self.const_qualified - - def resolve_refs(self, resolver): - if not self.return_type is None: - self.return_type.resolve_refs(resolver) - - if not self.declaration is None: - return - - if 'name' in self.node.attrib: - name = self.node.attrib['name'] - self.declaration = resolver(name) - - if self.spelling == '' and not self.declaration is None: - self.spelling = self.declaration.spelling - - if self.declaration.typename in ['record', 'class', 'interface']: - self.spelling += ' *' - self.kind = cindex.TypeKind.POINTER + builtins = [ + 'utf8', + 'gchar', + 'gint', + 'gint8', + 'gint16', + 'gint32', + 'gint64', + 'guint', + 'guint8', + 'guint16', + 'guint32', + 'guint64', + 'gfloat', + 'gdouble', + 'gpointer', + 'gsize', + 'gboolean', + 'none' + ]; + + def __init__(self, node): + self.node = node + self.kind = cindex.TypeKind.UNEXPOSED + self.const_qualified = False + + self.is_out = False + self.transfer_ownership = 'none' + self.allow_none = False + + aname = nsc('type') + + if aname in self.node.attrib: + self.spelling = self.node.attrib[aname] + else: + self.spelling = '' + + self._extract_const() + self._extract_kind() + self.declaration = None + + retval = self.node.find(nsgtk('return-value')) + + if not retval is None: + self.return_type = GirCursor(retval).type + + if 'transfer-ownership' in retval.attrib: + self.return_type.transfer_ownership = retval.attrib['transfer-ownership'] + + if 'allow-none' in retval.attrib: + self.return_type.allow_none = retval.attrib['allow-none'] == '1' + else: + self.return_type = None + + def is_builtin(self): + return self.spelling in GirType.builtins + + def _extract_const(self): + prefix = 'const ' + + if self.spelling.startswith(prefix): + self.const_qualified = True + self.spelling = self.spelling[len(prefix):] + + def _extract_kind(self): + if self.spelling == '': + return + + if self.spelling.endswith('*'): + self.kind = cindex.TypeKind.POINTER + return + + for k in nodes.Type.namemap: + if nodes.Type.namemap[k] == self.spelling: + self.kind = k + break + + def get_pointee(self): + return GirTypePointer(self) + + def get_result(self): + return self.return_type + + def get_canonical(self): + return self + + def get_declaration(self): + return self.declaration + + def is_const_qualified(self): + return self.const_qualified + + def resolve_refs(self, resolver): + if not self.return_type is None: + self.return_type.resolve_refs(resolver) + + if not self.declaration is None: + return + + if 'name' in self.node.attrib: + name = self.node.attrib['name'] + self.declaration = resolver(name) + + if self.spelling == '' and not self.declaration is None: + self.spelling = self.declaration.spelling + + if self.declaration.typename in ['record', 'class', 'interface']: + self.spelling += ' *' + self.kind = cindex.TypeKind.POINTER - elif self.spelling == '' and name in GirType.builtins: - if name == 'utf8': - self.spelling = 'gchar *' - elif name == 'none': - self.spelling = 'void' - else: - self.spelling = name + elif self.spelling == '' and name in GirType.builtins: + if name == 'utf8': + self.spelling = 'gchar *' + elif name == 'none': + self.spelling = 'void' + else: + self.spelling = name class GirTypePointer(GirType): - def __init__(self, tp): - self.node = tp.node - self.pointer_type = tp - self.spelling = tp.spelling[:-1] - self.kind = cindex.TypeKind.UNEXPOSED - self.const_qualified = False + def __init__(self, tp): + self.node = tp.node + self.pointer_type = tp + self.spelling = tp.spelling[:-1] + self.kind = cindex.TypeKind.UNEXPOSED + self.const_qualified = False - self._extract_const() - self._extract_kind() + self._extract_const() + self._extract_kind() - def get_declaration(self): - return self.pointer_type.get_declaration() + def get_declaration(self): + return self.pointer_type.get_declaration() class GirCursor: - kindmap = { - 'parameter': cindex.CursorKind.PARM_DECL - } + kindmap = { + 'parameter': cindex.CursorKind.PARM_DECL + } - global_gerror_param = None + global_gerror_param = None - def __init__(self, node): - self.node = node - self.typename = stripns(self.node.tag) - self.children = [] - self.parent = None - self.bases = None - self.implements = None + def __init__(self, node): + self.node = node + self.typename = stripns(self.node.tag) + self.children = [] + self.parent = None + self.bases = None + self.implements = None - if 'introspectable' in node.attrib: - self.introspectable = (node.attrib['introspectable'] != '0') - else: - self.introspectable = True + if 'introspectable' in node.attrib: + self.introspectable = (node.attrib['introspectable'] != '0') + else: + self.introspectable = True - self.type = self._extract_type() - self.kind = self._extract_kind() + self.type = self._extract_type() + self.kind = self._extract_kind() - self._virtual_param = None + self._virtual_param = None - if self._is_object_type(): - self._create_virtual_param() + if self._is_object_type(): + self._create_virtual_param() - if self.typename == 'member': - self.enum_value = node.attrib['value'] + if self.typename == 'member': + self.enum_value = node.attrib['value'] - self._extract_children() + self._extract_children() - def _extract_kind(self): - if self.typename in GirCursor.kindmap: - return GirCursor.kindmap[self.typename] - else: - return cindex.CursorKind.UNEXPOSED_DECL + def _extract_kind(self): + if self.typename in GirCursor.kindmap: + return GirCursor.kindmap[self.typename] + else: + return cindex.CursorKind.UNEXPOSED_DECL - def _extract_type(self): - if self.typename == 'type': - return GirType(self.node) + def _extract_type(self): + if self.typename == 'type': + return GirType(self.node) - t = self.node.find(nsgtk('type')) + t = self.node.find(nsgtk('type')) - if not t is None: - retval = GirType(t) + if not t is None: + retval = GirType(t) - if 'direction' in self.node.attrib: - retval.is_out = self.node.attrib['direction'] == 'out' + if 'direction' in self.node.attrib: + retval.is_out = self.node.attrib['direction'] == 'out' - if 'transfer-ownership' in self.node.attrib and not retval.is_out: - retval.transfer_ownership = self.node.attrib['transfer-ownership'] + if 'transfer-ownership' in self.node.attrib and not retval.is_out: + retval.transfer_ownership = self.node.attrib['transfer-ownership'] - if 'allow-none' in self.node.attrib: - retval.allow_none = self.node.attrib['allow-none'] == '1' + if 'allow-none' in self.node.attrib: + retval.allow_none = self.node.attrib['allow-none'] == '1' - return retval + return retval - va = self.node.find(nsgtk('varargs')) + va = self.node.find(nsgtk('varargs')) - if not va is None: - return GirType(va) + if not va is None: + return GirType(va) - ar = self.node.find(nsgtk('array')) + ar = self.node.find(nsgtk('array')) - if not ar is None: - return GirType(ar) + if not ar is None: + return GirType(ar) - ret = GirType(self.node) - ret.declaration = self + ret = GirType(self.node) + ret.declaration = self - return ret + return ret - def _is_object_type(self): - return self.typename in ['class', 'interface'] or \ - (self.typename == 'record' and nsglib('get-type') in self.node.attrib) + def _is_object_type(self): + return self.typename in ['class', 'interface'] or \ + (self.typename == 'record' and nsglib('get-type') in self.node.attrib) - def _create_virtual_param(self): - # Make virtual first parameter representing pointer to object - param = ElementTree.Element(nsgtk('parameter')) + def _create_virtual_param(self): + # Make virtual first parameter representing pointer to object + param = ElementTree.Element(nsgtk('parameter')) - param.attrib['name'] = 'self' - param.attrib['transfer-ownership'] = 'none' + param.attrib['name'] = 'self' + param.attrib['transfer-ownership'] = 'none' - ntp = nsc('type') + ntp = nsc('type') - tp = ElementTree.Element(nsgtk('type')) - tp.attrib['name'] = self.node.attrib['name'] - tp.attrib[ntp] = self.node.attrib[ntp] + '*' + tp = ElementTree.Element(nsgtk('type')) + tp.attrib['name'] = self.node.attrib['name'] + tp.attrib[ntp] = self.node.attrib[ntp] + '*' - doc = ElementTree.Element(nsgtk('doc')) - doc.text = 'a <{0}>.'.format(self.node.attrib[ntp]) + doc = ElementTree.Element(nsgtk('doc')) + doc.text = 'a <{0}>.'.format(self.node.attrib[ntp]) - param.append(doc) - param.append(tp) + param.append(doc) + param.append(tp) - self._virtual_param = param + self._virtual_param = param - def _setup_first_param(self, method): - method.children.insert(0, GirCursor(self._virtual_param)) + def _setup_first_param(self, method): + method.children.insert(0, GirCursor(self._virtual_param)) - def _make_gerror_param(self): - if not GirCursor.global_gerror_param is None: - return GirCursor.global_gerror_param + def _make_gerror_param(self): + if not GirCursor.global_gerror_param is None: + return GirCursor.global_gerror_param - param = ElementTree.Element(nsgtk('parameter')) + param = ElementTree.Element(nsgtk('parameter')) - param.attrib['name'] = 'error' - param.attrib['transfer-ownership'] = 'none' - param.attrib['allow-none'] = '1' + param.attrib['name'] = 'error' + param.attrib['transfer-ownership'] = 'none' + param.attrib['allow-none'] = '1' - tp = ElementTree.Element(nsgtk('type')) + tp = ElementTree.Element(nsgtk('type')) - tp.attrib['name'] = 'Error' - tp.attrib[nsc('type')] = 'GError **' + tp.attrib['name'] = 'Error' + tp.attrib[nsc('type')] = 'GError **' - doc = ElementTree.Element(nsgtk('doc')) - doc.text = 'a #GError.' + doc = ElementTree.Element(nsgtk('doc')) + doc.text = 'a #GError.' - param.append(doc) - param.append(tp) + param.append(doc) + param.append(tp) - GirCursor.global_gerror_param = param - return param + GirCursor.global_gerror_param = param + return param - def _extract_children(self): - children = [] + def _extract_children(self): + children = [] - if self.typename in ['function', 'method', 'virtual-method', 'constructor']: - children = list(self.node.iterfind(nsgtk('parameters') + '/' + nsgtk('parameter'))) + if self.typename in ['function', 'method', 'virtual-method', 'constructor']: + children = list(self.node.iterfind(nsgtk('parameters') + '/' + nsgtk('parameter'))) - if 'throws' in self.node.attrib and self.node.attrib['throws'] == '1': - children.append(self._make_gerror_param()) + if 'throws' in self.node.attrib and self.node.attrib['throws'] == '1': + children.append(self._make_gerror_param()) - elif self.typename in ['enumeration', 'bitfield']: - children = self.node.iterfind(nsgtk('member')) - elif self.typename in ['record', 'class', 'interface']: - self.bases = [] - self.implements = [] + elif self.typename in ['enumeration', 'bitfield']: + children = self.node.iterfind(nsgtk('member')) + elif self.typename in ['record', 'class', 'interface']: + self.bases = [] + self.implements = [] - def childgen(): - childtypes = ['function', 'method', 'constructor', 'virtual-method', 'property', 'field'] + def childgen(): + childtypes = ['function', 'method', 'constructor', 'virtual-method', 'property', 'field'] - for child in self.node: - if stripns(child.tag) in childtypes: - yield child + for child in self.node: + if stripns(child.tag) in childtypes: + yield child - children = childgen() + children = childgen() - for child in children: - cursor = GirCursor(child) + for child in children: + cursor = GirCursor(child) - if not self._virtual_param is None and \ - cursor.typename == 'method' or cursor.typename == 'virtual-method': - self._setup_first_param(cursor) + if not self._virtual_param is None and \ + cursor.typename == 'method' or cursor.typename == 'virtual-method': + self._setup_first_param(cursor) - cursor.parent = self - self.children.append(cursor) + cursor.parent = self + self.children.append(cursor) - @property - def displayname(self): - return self.name + @property + def displayname(self): + return self.name - @property - def semantic_parent(self): - return self.parent + @property + def semantic_parent(self): + return self.parent - @property - def spelling(self): - if self.typename in ['function', 'method', 'member', 'constructor']: - n = nsc('identifier') - elif self.typename in ['parameter', 'field', 'property']: - n = 'name' - else: - n = nsc('type') + @property + def spelling(self): + if self.typename in ['function', 'method', 'member', 'constructor']: + n = nsc('identifier') + elif self.typename in ['parameter', 'field', 'property']: + n = 'name' + else: + n = nsc('type') - if n in self.node.attrib: - return self.node.attrib[n] - else: - return '' + if n in self.node.attrib: + return self.node.attrib[n] + else: + return '' - def is_static_method(self): - return False + def is_static_method(self): + return False - def is_virtual_method(self): - return self.typename == 'virtual-method' + def is_virtual_method(self): + return self.typename == 'virtual-method' - def is_definition(self): - return True + def is_definition(self): + return True - @property - def name(self): - return self.spelling + @property + def name(self): + return self.spelling - @property - def refname(self): - if nsglib('type-name') in self.node.attrib and 'name' in self.node.attrib: - return self.node.attrib['name'] - else: - return None + @property + def refname(self): + if nsglib('type-name') in self.node.attrib and 'name' in self.node.attrib: + return self.node.attrib['name'] + else: + return None - @property - def extent(self): - return None + @property + def extent(self): + return None - @property - def location(self): - return None + @property + def location(self): + return None - def get_children(self): - return self.children + def get_children(self): + return self.children - def _add_base(self, b): - if not b is None: - self.bases.append(b) + def _add_base(self, b): + if not b is None: + self.bases.append(b) - def _add_implements(self, i): - if not i is None: - self.implements.append(i) + def _add_implements(self, i): + if not i is None: + self.implements.append(i) - def get_usr(self): - return self.spelling + def get_usr(self): + return self.spelling - def resolve_refs(self, resolver): - # Resolve things like types and stuff - if not self.type is None: - self.type.resolve_refs(resolver) + def resolve_refs(self, resolver): + # Resolve things like types and stuff + if not self.type is None: + self.type.resolve_refs(resolver) - for child in self.children: - child.resolve_refs(resolver) + for child in self.children: + child.resolve_refs(resolver) - # What about, like, baseclasses... - if self.typename in ['class', 'interface']: - if 'parent' in self.node.attrib: - self._add_base(resolver(self.node.attrib['parent'])) + # What about, like, baseclasses... + if self.typename in ['class', 'interface']: + if 'parent' in self.node.attrib: + self._add_base(resolver(self.node.attrib['parent'])) - for implements in self.node.iterfind(nsgtk('implements')): - self._add_implements(resolver(implements.attrib['name'])) + for implements in self.node.iterfind(nsgtk('implements')): + self._add_implements(resolver(implements.attrib['name'])) class GirTree(documentmerger.DocumentMerger): - def __init__(self, category=None): - self.mapping = { - 'function': self.parse_function, - 'class': self.parse_class, - 'record': self.parse_record, - 'interface': self.parse_interface, - 'enumeration': self.parse_enumeration, - 'callback': self.parse_callback, - 'bitfield': self.parse_enumeration, - 'virtual-method': self.parse_virtual_method, - 'method': self.parse_method, - 'constructor': self.parse_constructor, - 'property': self.parse_property, - 'signal': self.parse_signal, - 'field': self.parse_field, - 'doc': None, - 'implements': None, - 'prerequisite': None, - } - - self.category_to_node = defdict.Defdict() - - self.root = nodes.Root() - self.namespaces = {} - self.processed = {} - self.map_id_to_cusor = {} - self.cursor_to_node = {} - self.exported_namespaces = [] - self.usr_to_node = defdict.Defdict() - self.qid_to_node = defdict.Defdict() - self.all_nodes = [] - - self.usr_to_node[None] = self.root - self.qid_to_node[None] = self.root - - if not category is None: - self.category = self.add_categories([category]) - else: - self.category = None - - if not self.category is None: - self.root_node = self.category - else: - self.root_node = self.root + def __init__(self, category=None): + self.mapping = { + 'function': self.parse_function, + 'class': self.parse_class, + 'record': self.parse_record, + 'interface': self.parse_interface, + 'enumeration': self.parse_enumeration, + 'callback': self.parse_callback, + 'bitfield': self.parse_enumeration, + 'virtual-method': self.parse_virtual_method, + 'method': self.parse_method, + 'constructor': self.parse_constructor, + 'property': self.parse_property, + 'signal': self.parse_signal, + 'field': self.parse_field, + 'doc': None, + 'implements': None, + 'prerequisite': None, + } + + self.category_to_node = defdict.Defdict() + + self.root = nodes.Root() + self.namespaces = {} + self.processed = {} + self.map_id_to_cusor = {} + self.cursor_to_node = {} + self.exported_namespaces = [] + self.usr_to_node = defdict.Defdict() + self.qid_to_node = defdict.Defdict() + self.all_nodes = [] + + self.usr_to_node[None] = self.root + self.qid_to_node[None] = self.root + + if not category is None: + self.category = self.add_categories([category]) + else: + self.category = None + + if not self.category is None: + self.root_node = self.category + else: + self.root_node = self.root - def match_ref(self, child, name): - if isinstance(name, utf8.string): - return name == child.name - else: - return name.match(child.name) + def match_ref(self, child, name): + if isinstance(name, utf8.string): + return name == child.name + else: + return name.match(child.name) - def find_ref(self, node, name, goup): - if node is None: - return [] + def find_ref(self, node, name, goup): + if node is None: + return [] - ret = [] + ret = [] - for child in node.resolve_nodes: - if self.match_ref(child, name): - ret.append(child) + for child in node.resolve_nodes: + if self.match_ref(child, name): + ret.append(child) - if goup and len(ret) == 0: - return self.find_ref(node.parent, name, True) - else: - return ret + if goup and len(ret) == 0: + return self.find_ref(node.parent, name, True) + else: + return ret - def cross_ref(self, node=None): - if node is None: - node = self.root + def cross_ref(self, node=None): + if node is None: + node = self.root - if not node.comment is None: - node.comment.resolve_refs(self.find_ref, node) + if not node.comment is None: + node.comment.resolve_refs(self.find_ref, node) - for child in node.children: - self.cross_ref(child) + for child in node.children: + self.cross_ref(child) - self.markup_code() + self.markup_code() - def parse_function(self, cursor): - return nodes.Function(cursor, GirComment(cursor)) + def parse_function(self, cursor): + return nodes.Function(cursor, GirComment(cursor)) - def parse_struct_children(self, ret): - for child in ret.cursor.children: - c = self.parse_cursor(child) + def parse_struct_children(self, ret): + for child in ret.cursor.children: + c = self.parse_cursor(child) - if not c is None: - ret.append(c) + if not c is None: + ret.append(c) - def parse_class(self, cursor): - ret = Class(cursor, GirComment(cursor)) + def parse_class(self, cursor): + ret = Class(cursor, GirComment(cursor)) - ret.typedef = nodes.Typedef(cursor, None) - self.parse_struct_children(ret) + ret.typedef = nodes.Typedef(cursor, None) + self.parse_struct_children(ret) - return ret + return ret - def parse_signal(self, node): - # TODO - return None + def parse_signal(self, node): + # TODO + return None - def parse_field(self, cursor): - if 'private' in cursor.node.attrib and cursor.node.attrib['private'] == '1': - return None + def parse_field(self, cursor): + if 'private' in cursor.node.attrib and cursor.node.attrib['private'] == '1': + return None - return nodes.Field(cursor, GirComment(cursor)) + return nodes.Field(cursor, GirComment(cursor)) - def parse_constructor(self, cursor): - return nodes.Function(cursor, GirComment(cursor)) + def parse_constructor(self, cursor): + return nodes.Function(cursor, GirComment(cursor)) - def parse_virtual_method(self, node): - # TODO - return None + def parse_virtual_method(self, node): + # TODO + return None - def parse_method(self, cursor): - return nodes.Function(cursor, GirComment(cursor)) + def parse_method(self, cursor): + return nodes.Function(cursor, GirComment(cursor)) - def parse_property(self, cursor): - return Property(cursor, GirComment(cursor)) + def parse_property(self, cursor): + return Property(cursor, GirComment(cursor)) - def parse_boxed(self, cursor): - ret = Boxed(cursor, GirComment(cursor)) - ret.typedef = nodes.Typedef(cursor, None) + def parse_boxed(self, cursor): + ret = Boxed(cursor, GirComment(cursor)) + ret.typedef = nodes.Typedef(cursor, None) - self.parse_struct_children(ret) - return ret + self.parse_struct_children(ret) + return ret - def parse_record(self, cursor): - if nsglib('is-gtype-struct-for') in cursor.node.attrib: - return None + def parse_record(self, cursor): + if nsglib('is-gtype-struct-for') in cursor.node.attrib: + return None - if 'disguised' in cursor.node.attrib and cursor.node.attrib['disguised'] == '1': - return None + if 'disguised' in cursor.node.attrib and cursor.node.attrib['disguised'] == '1': + return None - if nsglib('get-type') in cursor.node.attrib: - return self.parse_boxed(cursor) + if nsglib('get-type') in cursor.node.attrib: + return self.parse_boxed(cursor) - ret = nodes.Struct(cursor, GirComment(cursor)) - ret.typedef = nodes.Typedef(cursor, None) + ret = nodes.Struct(cursor, GirComment(cursor)) + ret.typedef = nodes.Typedef(cursor, None) - self.parse_struct_children(ret) + self.parse_struct_children(ret) - return ret + return ret - def parse_interface(self, cursor): - ret = Interface(cursor, GirComment(cursor)) - self.parse_struct_children(ret) + def parse_interface(self, cursor): + ret = Interface(cursor, GirComment(cursor)) + self.parse_struct_children(ret) - return ret + return ret - def parse_enumeration(self, cursor): - ret = nodes.Enum(cursor, GirComment(cursor)) + def parse_enumeration(self, cursor): + ret = nodes.Enum(cursor, GirComment(cursor)) - # All enums are typedefs - ret.typedef = nodes.Typedef(cursor, None) + # All enums are typedefs + ret.typedef = nodes.Typedef(cursor, None) - for member in cursor.children: - ret.append(nodes.EnumValue(member, GirComment(member))) + for member in cursor.children: + ret.append(nodes.EnumValue(member, GirComment(member))) - return ret + return ret - def parse_callback(self, cursor): - pass + def parse_callback(self, cursor): + pass - def parse_cursor(self, cursor): - if not cursor.introspectable: - return None + def parse_cursor(self, cursor): + if not cursor.introspectable: + return None - fn = self.mapping[cursor.typename] + fn = self.mapping[cursor.typename] - if not fn is None: - ret = fn(cursor) + if not fn is None: + ret = fn(cursor) - if not ret is None: - self.cursor_to_node[cursor] = ret - self.all_nodes.append(ret) + if not ret is None: + self.cursor_to_node[cursor] = ret + self.all_nodes.append(ret) - return ret - else: - return None + return ret + else: + return None - def lookup_gir(self, ns, version): - dirs = os.getenv('XDG_DATA_DIRS') + def lookup_gir(self, ns, version): + dirs = os.getenv('XDG_DATA_DIRS') - if dirs is None: - dirs = ['/usr/local/share', '/usr/share'] - else: - dirs = dirs.split(os.pathsep) + if dirs is None: + dirs = ['/usr/local/share', '/usr/share'] + else: + dirs = dirs.split(os.pathsep) - for d in dirs: - fname = os.path.join(d, 'gir-1.0', "{0}-{1}.gir".format(ns, version)) + for d in dirs: + fname = os.path.join(d, 'gir-1.0', "{0}-{1}.gir".format(ns, version)) - if os.path.exists(fname): - return fname + if os.path.exists(fname): + return fname - return None + return None - def gir_split(self, filename): - name, _ = os.path.splitext(os.path.basename(filename)) - return name.split('-', 2) + def gir_split(self, filename): + name, _ = os.path.splitext(os.path.basename(filename)) + return name.split('-', 2) - def add_gir(self, filename, included=False): - ns, version = self.gir_split(filename) + def add_gir(self, filename, included=False): + ns, version = self.gir_split(filename) - if (ns, version) in self.processed: - return + if (ns, version) in self.processed: + return - tree = ElementTree.parse(filename) - repository = tree.getroot() + tree = ElementTree.parse(filename) + repository = tree.getroot() - self.processed[(ns, version)] = tree + self.processed[(ns, version)] = tree - # First process includes - for include in repository.iterfind(nsgtk('include')): - incname = include.attrib['name'] - incversion = include.attrib['version'] + # First process includes + for include in repository.iterfind(nsgtk('include')): + incname = include.attrib['name'] + incversion = include.attrib['version'] - filename = self.lookup_gir(incname, incversion) + filename = self.lookup_gir(incname, incversion) - if filename is None: - sys.stderr.write('Could not find include `{0}-{1}\'\n'.format(incname, incversion)) - sys.exit(1) + if filename is None: + sys.stderr.write('Could not find include `{0}-{1}\'\n'.format(incname, incversion)) + sys.exit(1) - self.add_gir(filename, True) + self.add_gir(filename, True) - # Then process cursors - ns = repository.find(nsgtk('namespace')) - nsname = ns.attrib['name'] + # Then process cursors + ns = repository.find(nsgtk('namespace')) + nsname = ns.attrib['name'] - cursors = [] + cursors = [] - for child in ns: - cursor = GirCursor(child) - refname = cursor.refname + for child in ns: + cursor = GirCursor(child) + refname = cursor.refname - if not refname is None: - self.map_id_to_cusor[nsname + '.' + refname] = cursor + if not refname is None: + self.map_id_to_cusor[nsname + '.' + refname] = cursor - cursors.append(cursor) + cursors.append(cursor) - self.namespaces[nsname] = cursors + self.namespaces[nsname] = cursors - if not included: - self.exported_namespaces.append(nsname) + if not included: + self.exported_namespaces.append(nsname) - def resolve_ref(self, ns): - def resolver(item): - item = item.rstrip('*') + def resolve_ref(self, ns): + def resolver(item): + item = item.rstrip('*') - if item in GirType.builtins: - return None + if item in GirType.builtins: + return None - if not '.' in item: - item = ns + '.' + item + if not '.' in item: + item = ns + '.' + item - if item in self.map_id_to_cusor: - return self.map_id_to_cusor[item] - else: - return None + if item in self.map_id_to_cusor: + return self.map_id_to_cusor[item] + else: + return None - return resolver + return resolver - def parse(self): - # Resolve cursor references - for ns in self.namespaces: - for cursor in self.namespaces[ns]: - cursor.resolve_refs(self.resolve_ref(ns)) + def parse(self): + # Resolve cursor references + for ns in self.namespaces: + for cursor in self.namespaces[ns]: + cursor.resolve_refs(self.resolve_ref(ns)) - classes = {} + classes = {} - for ns in self.exported_namespaces: - for cursor in self.namespaces[ns]: - node = self.parse_cursor(cursor) + for ns in self.exported_namespaces: + for cursor in self.namespaces[ns]: + node = self.parse_cursor(cursor) - if not node is None: - self.root_node.append(node) + if not node is None: + self.root_node.append(node) - if isinstance(node, Class) or isinstance(node, Interface): - classes[node.qid] = node + if isinstance(node, Class) or isinstance(node, Interface): + classes[node.qid] = node - for qid in classes: - classes[qid].resolve_bases(classes) + for qid in classes: + classes[qid].resolve_bases(classes) - for node in self.all_nodes: - self.qid_to_node[node.qid] = node + for node in self.all_nodes: + self.qid_to_node[node.qid] = node - def markup_code(self): - for node in self.all_nodes: - if node.comment is None: - continue + def markup_code(self): + for node in self.all_nodes: + if node.comment is None: + continue - if not node.comment.doc: - continue + if not node.comment.doc: + continue - comps = node.comment.doc.components + comps = node.comment.doc.components - for i in range(len(comps)): - component = comps[i] + for i in range(len(comps)): + component = comps[i] - if not isinstance(component, comment.Comment.Example): - continue + if not isinstance(component, comment.Comment.Example): + continue - text = str(component) + text = str(component) - ex = example.Example() - ex.append(text) + ex = example.Example() + ex.append(text) - comps[i] = ex + comps[i] = ex def run(args): - parser = argparse.ArgumentParser(description='clang based documentation generator.', - usage='%(prog)s gir --output DIR [OPTIONS] GIRFILE') + parser = argparse.ArgumentParser(description='clang based documentation generator.', + usage='%(prog)s gir --output DIR [OPTIONS] GIRFILE') - parser.add_argument('--quiet', default=False, action='store_const', const=True, - help='be quiet about it') + parser.add_argument('--quiet', default=False, action='store_const', const=True, + help='be quiet about it') - parser.add_argument('--report', default=False, - action='store_const', const=True, help='report documentation coverage and errors') + parser.add_argument('--report', default=False, + action='store_const', const=True, help='report documentation coverage and errors') - parser.add_argument('--output', default=None, metavar='DIR', - help='specify the output directory') + parser.add_argument('--output', default=None, metavar='DIR', + help='specify the output directory') - parser.add_argument('--type', default='html', metavar='TYPE', - help='specify the type of output (html or xml, default html)') + parser.add_argument('--type', default='html', metavar='TYPE', + help='specify the type of output (html or xml, default html)') - parser.add_argument('--merge', default=[], metavar='FILES', action='append', - help='specify additional description files to merge into the documentation') + parser.add_argument('--merge', default=[], metavar='FILES', action='append', + help='specify additional description files to merge into the documentation') - parser.add_argument('--merge-filter', default=None, metavar='FILTER', - help='specify program to pass merged description files through') + parser.add_argument('--merge-filter', default=None, metavar='FILTER', + help='specify program to pass merged description files through') - parser.add_argument('--static', default=False, action='store_const', const=True, - help='generate a static website (only for when --output is html)') + parser.add_argument('--static', default=False, action='store_const', const=True, + help='generate a static website (only for when --output is html)') - parser.add_argument('--category', default=None, metavar='CATEGORY', - help='category in which to place all symbols') + parser.add_argument('--category', default=None, metavar='CATEGORY', + help='category in which to place all symbols') - parser.add_argument('--custom-js', default=[], metavar='FILES', action='append', - help='specify additional javascript files to be merged into the html (only for when --output is html)') + parser.add_argument('--custom-js', default=[], metavar='FILES', action='append', + help='specify additional javascript files to be merged into the html (only for when --output is html)') - parser.add_argument('--custom-css', default=[], metavar='FILES', action='append', - help='specify additional css files to be merged into the html (only for when --output is html)') + parser.add_argument('--custom-css', default=[], metavar='FILES', action='append', + help='specify additional css files to be merged into the html (only for when --output is html)') - parser.add_argument('files', nargs='+', help='gir files to parse') + parser.add_argument('files', nargs='+', help='gir files to parse') - opts = parser.parse_args(args) + opts = parser.parse_args(args) - t = GirTree(opts.category) + t = GirTree(opts.category) - # Generate artificial tree - for f in opts.files: - t.add_gir(f) + # Generate artificial tree + for f in opts.files: + t.add_gir(f) - t.parse() + t.parse() - if opts.merge: - t.merge(opts.merge_filter, opts.merge) + if opts.merge: + t.merge(opts.merge_filter, opts.merge) - t.cross_ref() + t.cross_ref() - from .cmdgenerate import run_generate + from .cmdgenerate import run_generate - run_generate(t, opts) + run_generate(t, opts) # vi:ts=4:et diff --git a/cldoc/cmdinspect.py b/cldoc/cmdinspect.py index 4960461..c5730a5 100644 --- a/cldoc/cmdinspect.py +++ b/cldoc/cmdinspect.py @@ -15,29 +15,29 @@ import sys, argparse def run(args): - try: - sep = args.index('--') - except ValueError: - if not '--help' in args: - sys.stderr.write('Please use: cldoc inspect [CXXFLAGS] -- [OPTIONS] [FILES]\n') - sys.exit(1) - else: - sep = 0 + try: + sep = args.index('--') + except ValueError: + if not '--help' in args: + sys.stderr.write('Please use: cldoc inspect [CXXFLAGS] -- [OPTIONS] [FILES]\n') + sys.exit(1) + else: + sep = 0 - parser = argparse.ArgumentParser(description='clang based documentation generator.', - usage='%(prog)s inspect [CXXFLAGS] -- [OPTIONS] DIRECTORY') + parser = argparse.ArgumentParser(description='clang based documentation generator.', + usage='%(prog)s inspect [CXXFLAGS] -- [OPTIONS] DIRECTORY') - parser.add_argument('files', nargs='*', help='files to parse') + parser.add_argument('files', nargs='*', help='files to parse') - restargs = args[sep + 1:] - cxxflags = args[:sep] + restargs = args[sep + 1:] + cxxflags = args[:sep] - opts = parser.parse_args(restargs) + opts = parser.parse_args(restargs) - from . import tree - from . import inspecttree + from . import tree + from . import inspecttree - t = tree.Tree(opts.files, cxxflags) - inspecttree.inspect(t) + t = tree.Tree(opts.files, cxxflags) + inspecttree.inspect(t) # vi:ts=4:et diff --git a/cldoc/cmdserve.py b/cldoc/cmdserve.py index 641c1ca..9bb3068 100644 --- a/cldoc/cmdserve.py +++ b/cldoc/cmdserve.py @@ -16,81 +16,81 @@ import SimpleHTTPServer, SocketServer class Server(SocketServer.TCPServer): - allow_reuse_address = True + allow_reuse_address = True def handler_bind(directory): - class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): - def end_headers(self): - self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate') - self.send_header('Pragma', 'no-cache') - self.send_header('Expires', '0') + class Handler(SimpleHTTPServer.SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate') + self.send_header('Pragma', 'no-cache') + self.send_header('Expires', '0') - SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) + SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self) - def translate_path(self, path): - while path.startswith('/'): - path = path[1:] + def translate_path(self, path): + while path.startswith('/'): + path = path[1:] - path = os.path.join(directory, path) - return SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path) + path = os.path.join(directory, path) + return SimpleHTTPServer.SimpleHTTPRequestHandler.translate_path(self, path) - def log_message(self, format, *args): - pass + def log_message(self, format, *args): + pass - return Handler + return Handler class SocketThread(threading.Thread): - def __init__(self, directory, host): - threading.Thread.__init__(self) + def __init__(self, directory, host): + threading.Thread.__init__(self) - if not ':' in host: - self.host = host - self.port = 6060 - else: - self.host, port = host.split(':') - self.port = int(port) + if not ':' in host: + self.host = host + self.port = 6060 + else: + self.host, port = host.split(':') + self.port = int(port) - self.httpd = Server((self.host, self.port), handler_bind(directory)) + self.httpd = Server((self.host, self.port), handler_bind(directory)) - def shutdown(self): - self.httpd.shutdown() - self.httpd.server_close() + def shutdown(self): + self.httpd.shutdown() + self.httpd.server_close() - def run(self): - self.httpd.serve_forever() + def run(self): + self.httpd.serve_forever() def run(args): - parser = argparse.ArgumentParser(description='clang based documentation generator.', - usage='%(prog)s serve [OPTIONS] [DIRECTORY]') + parser = argparse.ArgumentParser(description='clang based documentation generator.', + usage='%(prog)s serve [OPTIONS] [DIRECTORY]') - parser.add_argument('--address', default=':6060', metavar='HOST:PORT', - help='address (host:port) on which to serve documentation') + parser.add_argument('--address', default=':6060', metavar='HOST:PORT', + help='address (host:port) on which to serve documentation') - parser.add_argument('directory', nargs='?', help='directory to serve', default='.') + parser.add_argument('directory', nargs='?', help='directory to serve', default='.') - opts = parser.parse_args(args) + opts = parser.parse_args(args) - t = SocketThread(opts.directory, opts.address) - t.start() + t = SocketThread(opts.directory, opts.address) + t.start() - dn = open(os.devnull, 'w') + dn = open(os.devnull, 'w') - if t.host == '': - url = 'http://localhost:{0}/'.format(t.port) - else: - url = 'http://{0}:{1}/'.format(t.host, t.port) + if t.host == '': + url = 'http://localhost:{0}/'.format(t.port) + else: + url = 'http://{0}:{1}/'.format(t.host, t.port) - if sys.platform.startswith('darwin'): - subprocess.call(('open', url), stdout=dn, stderr=dn) - elif os.name == 'posix': - subprocess.call(('xdg-open', url), stdout=dn, stderr=dn) + if sys.platform.startswith('darwin'): + subprocess.call(('open', url), stdout=dn, stderr=dn) + elif os.name == 'posix': + subprocess.call(('xdg-open', url), stdout=dn, stderr=dn) - while True: - try: - time.sleep(3600) - except KeyboardInterrupt: - t.shutdown() - t.join() - break + while True: + try: + time.sleep(3600) + except KeyboardInterrupt: + t.shutdown() + t.join() + break # vi:ts=4:et diff --git a/cldoc/comment.py b/cldoc/comment.py index 9566b1f..a71b51e 100644 --- a/cldoc/comment.py +++ b/cldoc/comment.py @@ -19,562 +19,562 @@ import os, re, sys, bisect class Sorted(list): - def __init__(self, key=None): - if key is None: - key = lambda x: x + def __init__(self, key=None): + if key is None: + key = lambda x: x - self.keys = [] - self.key = key + self.keys = [] + self.key = key - def insert_bisect(self, item, bi): - k = self.key(item) - idx = bi(self.keys, k) + def insert_bisect(self, item, bi): + k = self.key(item) + idx = bi(self.keys, k) - self.keys.insert(idx, k) - return super(Sorted, self).insert(idx, item) + self.keys.insert(idx, k) + return super(Sorted, self).insert(idx, item) - def insert(self, item): - return self.insert_bisect(item, bisect.bisect_left) + def insert(self, item): + return self.insert_bisect(item, bisect.bisect_left) - insert_left = insert + insert_left = insert - def insert_right(self, item): - return self.insert_bisect(item, bisect.bisect_right) + def insert_right(self, item): + return self.insert_bisect(item, bisect.bisect_right) - def bisect(self, item, bi): - k = self.key(item) + def bisect(self, item, bi): + k = self.key(item) - return bi(self.keys, k) + return bi(self.keys, k) - def bisect_left(self, item): - return self.bisect(item, bisect.bisect_left) + def bisect_left(self, item): + return self.bisect(item, bisect.bisect_left) - def bisect_right(self, item): - return self.bisect(item, bisect.bisect_right) + def bisect_right(self, item): + return self.bisect(item, bisect.bisect_right) - def find(self, key): - i = bisect.bisect_left(self.keys, key) + def find(self, key): + i = bisect.bisect_left(self.keys, key) - if i != len(self.keys) and self.keys[i] == key: - return self[i] - else: - return None + if i != len(self.keys) and self.keys[i] == key: + return self[i] + else: + return None class Comment(object): - class Example(str): - def __new__(self, s, strip=True): - if strip: - s = '\n'.join([self._strip_prefix(x) for x in s.split('\n')]) + class Example(str): + def __new__(self, s, strip=True): + if strip: + s = '\n'.join([self._strip_prefix(x) for x in s.split('\n')]) - return str.__new__(self, s) + return str.__new__(self, s) - @staticmethod - def _strip_prefix(s): - if s.startswith(' '): - return s[4:] - else: - return s + @staticmethod + def _strip_prefix(s): + if s.startswith(' '): + return s[4:] + else: + return s - class String(object): - def __init__(self, s): - self.components = [utf8.utf8(s)] + class String(object): + def __init__(self, s): + self.components = [utf8.utf8(s)] - def _utf8(self): - return utf8.utf8("").join([utf8.utf8(x) for x in self.components]) + def _utf8(self): + return utf8.utf8("").join([utf8.utf8(x) for x in self.components]) - def __str__(self): - return str(self._utf8()) + def __str__(self): + return str(self._utf8()) - def __unicode__(self): - return unicode(self._utf8()) + def __unicode__(self): + return unicode(self._utf8()) - def __bytes__(self): - return bytes(self._utf8()) - - def __eq__(self, other): - if isinstance(other, str): - return str(self) == other - elif isinstance(other, unicode): - return unicode(self) == other - elif isinstance(other, bytes): - return bytes(self) == other - else: - return object.__cmp__(self, other) - - def __nonzero__(self): - l = len(self.components) - - return l > 0 and (l > 1 or len(self.components[0]) > 0) - - class MarkdownCode(utf8.utf8): - pass - - class UnresolvedReference(utf8.utf8): - reescape = re.compile('[*_]', re.I) - - def __new__(cls, s): - ns = Comment.UnresolvedReference.reescape.sub(lambda x: '\\' + x.group(0), s) - ret = utf8.utf8.__new__(cls, utf8.utf8('<{0}>').format(utf8.utf8(ns))) - - ret.orig = s - return ret - - redocref = re.compile('(?P[$]?)<(?:\\[(?P[^\\]]*)\\])?(?Poperator(?:>>|>|>=)|[^>\n]+)>') - redoccode = re.compile('\\\\code(.*\\n?)\\\\endcode', re.M | re.S) - #redoccode = re.compile('^ \\[code\\]\n(?P(?:(?: .*|)\n)*)', re.M) - redocmcode = re.compile('(^ *(`{3,}|~{3,}).*?\\2)', re.M | re.S) - - re_dox_ref=re.compile('\\\\(?P[a-zA-Z]+)\\s+(?P\\w+)(?:\\s+"(?P.*)")?') - - def __init__(self, text, location, opts): - self.__dict__['docstrings'] = [] - self.__dict__['text'] = text - - self.__dict__['location'] = location - self.__dict__['_resolved'] = False - - self.doc = text - self.brief = '' - self.images=[] - self.imagepaths=[] - self.options=opts - - def __setattr__(self, name, val): - if not name in self.docstrings: - self.docstrings.append(name) - - if isinstance(val, dict): - for key in val: - if not isinstance(val[key], Comment.String): - val[key] = Comment.String(val[key]) - elif not isinstance(val, Comment.String): - val = Comment.String(val) - - self.__dict__[name] = val - - def __nonzero__(self): - return (bool(self.brief) and not (self.brief == u'*documentation missing...*')) or (bool(self.doc) and not (self.doc == u'*documentation missing...*')) - - def redoccode_split(self, doc): - # Split on C/C++ code - components = Comment.redoccode.split(doc) - ret = [] - - for i in range(0, len(components), 2): - r = Comment.redocmcode.split(components[i]) - - for j in range(0, len(r), 3): - ret.append(r[j]) - - if j < len(r) - 1: - ret.append(Comment.MarkdownCode(r[j + 1])) - - if i < len(components) - 1: - ret.append(Comment.Example(components[i + 1])) - - return ret - - def redoc_split(self, doc): - ret = [] - - # First split examples - components = self.redoccode_split(doc) - line_offset=0 - for c in components: - if isinstance(c, Comment.Example) or isinstance(c, Comment.MarkdownCode): - ret.append((line_offset, c, None, None, None)) - line_offset+=c.count('\n') - else: - lastpos = 0 - pos=0 - while pos 0: - ref = re.compile(ref) - - elif dox_ref and min_pos==dox_ref.start(): - m=dox_ref - span = m.span(0) - prefix = c[lastpos:span[0]] - lastpos = span[1] - try: - command = m.group('command') - ref = m.group('ref') - refname = m.group('refname') - except: - pass - else: - prefix=c[lastpos:] - lastpos=len(c) - ret.append((line_offset,prefix,command,ref, refname)) - pos=lastpos - line_offset+=prefix.count('\n') - return ret - - def resolve_refs_for_doc(self, doc, resolver, root): - comps = self.redoc_split(utf8.utf8(doc)) - components = [] - - for pair in comps: - offset, prefix, command, name, refname = pair - components.append(prefix) - if command==None: - continue - lineno=int(0) - filename='' - if self.location: - if self.location.__class__.__name__=="SourceLocation": - filename=self.location.file.name - if self.location.line: - lineno=self.location.line - else: - filename=self.location - lineno=0 - lineno+=offset - if command=='ref': - if name is None: - continue - - if isinstance(name, utf8.string): - names = name.split('::') - else: - names = [name] - - nds = [root] - - for j in range(len(names)): - newnds = [] - - for n in nds: - newnds += resolver(n, names[j], j == 0) - - if len(newnds) == 0: - break - - nds = newnds - - if len(newnds) > 0: - components.append((newnds, refname)) - else: - components.append(Comment.UnresolvedReference(name)) - elif command=='a' or command=='em': - components.append('**'+name+'**') - elif command=='param' or command=='p': - components.append('\n**'+name+'**') - elif command=='return': - components.append('\n**return:** '+name) - elif command=='toc': - components.append('\n###Contents\n') - elif command=='brief': - pass - elif command=='image': - parts=os.path.split(filename); - path='' - if len(parts)>0: - path=parts[0] - imagepaths=[path] - found=False - for i in imagepaths: - filepath=i+'/'+refname - if os.path.exists(filepath): - found=True - break - if not found: - print(filename+' ('+str(lineno)+'): warning: Image not found: '+ filepath) - else: - components.append('[![alt text]("'+refname+' "'+refname+'")') - self.images.append(refname) - self.imagepaths.append(i) - else: - if filename=='': - print(': warning: Unknown command \\'+ command) - print(filename+' ('+str(lineno)+'): warning: Unknown command \\'+ command) - - doc.components = components - - def resolve_refs(self, resolver, root): - if self.__dict__['_resolved']: - return - - self.__dict__['_resolved'] = True - - for name in self.docstrings: - doc = getattr(self, name) - - if not doc: - continue - - if isinstance(doc, dict): - for key in doc: - if not isinstance(doc[key], Comment.String): - doc[key] = Comment.String(doc[key]) - - self.resolve_refs_for_doc(doc[key], resolver, root) - else: - self.resolve_refs_for_doc(doc, resolver, root) + def __bytes__(self): + return bytes(self._utf8()) + + def __eq__(self, other): + if isinstance(other, str): + return str(self) == other + elif isinstance(other, unicode): + return unicode(self) == other + elif isinstance(other, bytes): + return bytes(self) == other + else: + return object.__cmp__(self, other) + + def __nonzero__(self): + l = len(self.components) + + return l > 0 and (l > 1 or len(self.components[0]) > 0) + + class MarkdownCode(utf8.utf8): + pass + + class UnresolvedReference(utf8.utf8): + reescape = re.compile('[*_]', re.I) + + def __new__(cls, s): + ns = Comment.UnresolvedReference.reescape.sub(lambda x: '\\' + x.group(0), s) + ret = utf8.utf8.__new__(cls, utf8.utf8('<{0}>').format(utf8.utf8(ns))) + + ret.orig = s + return ret + + redocref = re.compile('(?P[$]?)<(?:\\[(?P[^\\]]*)\\])?(?Poperator(?:>>|>|>=)|[^>\n]+)>') + redoccode = re.compile('\\\\code(.*\\n?)\\\\endcode', re.M | re.S) + #redoccode = re.compile('^ \\[code\\]\n(?P(?:(?: .*|)\n)*)', re.M) + redocmcode = re.compile('(^ *(`{3,}|~{3,}).*?\\2)', re.M | re.S) + + re_dox_ref=re.compile('\\\\(?P[a-zA-Z]+)\\s+(?P\\w+)(?:\\s+"(?P.*)")?') + + def __init__(self, text, location, opts): + self.__dict__['docstrings'] = [] + self.__dict__['text'] = text + + self.__dict__['location'] = location + self.__dict__['_resolved'] = False + + self.doc = text + self.brief = '' + self.images=[] + self.imagepaths=[] + self.options=opts + + def __setattr__(self, name, val): + if not name in self.docstrings: + self.docstrings.append(name) + + if isinstance(val, dict): + for key in val: + if not isinstance(val[key], Comment.String): + val[key] = Comment.String(val[key]) + elif not isinstance(val, Comment.String): + val = Comment.String(val) + + self.__dict__[name] = val + + def __nonzero__(self): + return (bool(self.brief) and not (self.brief == u'*documentation missing...*')) or (bool(self.doc) and not (self.doc == u'*documentation missing...*')) + + def redoccode_split(self, doc): + # Split on C/C++ code + components = Comment.redoccode.split(doc) + ret = [] + + for i in range(0, len(components), 2): + r = Comment.redocmcode.split(components[i]) + + for j in range(0, len(r), 3): + ret.append(r[j]) + + if j < len(r) - 1: + ret.append(Comment.MarkdownCode(r[j + 1])) + + if i < len(components) - 1: + ret.append(Comment.Example(components[i + 1])) + + return ret + + def redoc_split(self, doc): + ret = [] + + # First split examples + components = self.redoccode_split(doc) + line_offset=0 + for c in components: + if isinstance(c, Comment.Example) or isinstance(c, Comment.MarkdownCode): + ret.append((line_offset, c, None, None, None)) + line_offset+=c.count('\n') + else: + lastpos = 0 + pos=0 + while pos 0: + ref = re.compile(ref) + + elif dox_ref and min_pos==dox_ref.start(): + m=dox_ref + span = m.span(0) + prefix = c[lastpos:span[0]] + lastpos = span[1] + try: + command = m.group('command') + ref = m.group('ref') + refname = m.group('refname') + except: + pass + else: + prefix=c[lastpos:] + lastpos=len(c) + ret.append((line_offset,prefix,command,ref, refname)) + pos=lastpos + line_offset+=prefix.count('\n') + return ret + + def resolve_refs_for_doc(self, doc, resolver, root): + comps = self.redoc_split(utf8.utf8(doc)) + components = [] + + for pair in comps: + offset, prefix, command, name, refname = pair + components.append(prefix) + if command==None: + continue + lineno=int(0) + filename='' + if self.location: + if self.location.__class__.__name__=="SourceLocation": + filename=self.location.file.name + if self.location.line: + lineno=self.location.line + else: + filename=self.location + lineno=0 + lineno+=offset + if command=='ref': + if name is None: + continue + + if isinstance(name, utf8.string): + names = name.split('::') + else: + names = [name] + + nds = [root] + + for j in range(len(names)): + newnds = [] + + for n in nds: + newnds += resolver(n, names[j], j == 0) + + if len(newnds) == 0: + break + + nds = newnds + + if len(newnds) > 0: + components.append((newnds, refname)) + else: + components.append(Comment.UnresolvedReference(name)) + elif command=='a' or command=='em': + components.append('**'+name+'**') + elif command=='param' or command=='p': + components.append('\n**'+name+'**') + elif command=='return': + components.append('\n**return:** '+name) + elif command=='toc': + components.append('\n###Contents\n') + elif command=='brief': + pass + elif command=='image': + parts=os.path.split(filename); + path='' + if len(parts)>0: + path=parts[0] + imagepaths=[path] + found=False + for i in imagepaths: + filepath=i+'/'+refname + if os.path.exists(filepath): + found=True + break + if not found: + print(filename+' ('+str(lineno)+'): warning: Image not found: '+ filepath) + else: + components.append('[![alt text]("'+refname+' "'+refname+'")') + self.images.append(refname) + self.imagepaths.append(i) + else: + if filename=='': + print(': warning: Unknown command \\'+ command) + print(filename+' ('+str(lineno)+'): warning: Unknown command \\'+ command) + + doc.components = components + + def resolve_refs(self, resolver, root): + if self.__dict__['_resolved']: + return + + self.__dict__['_resolved'] = True + + for name in self.docstrings: + doc = getattr(self, name) + + if not doc: + continue + + if isinstance(doc, dict): + for key in doc: + if not isinstance(doc[key], Comment.String): + doc[key] = Comment.String(doc[key]) + + self.resolve_refs_for_doc(doc[key], resolver, root) + else: + self.resolve_refs_for_doc(doc, resolver, root) class RangeMap(Sorted): - Item = Struct.define('Item', obj=None, start=0, end=0) + Item = Struct.define('Item', obj=None, start=0, end=0) - def __init__(self): - super(RangeMap, self).__init__(key=lambda x: x.start) + def __init__(self): + super(RangeMap, self).__init__(key=lambda x: x.start) - self.stack = [] + self.stack = [] - def push(self, obj, start): - self.stack.append(RangeMap.Item(obj=obj, start=start, end=start)) + def push(self, obj, start): + self.stack.append(RangeMap.Item(obj=obj, start=start, end=start)) - def pop(self, end): - item = self.stack.pop() - item.end = end + def pop(self, end): + item = self.stack.pop() + item.end = end - self.insert(item) + self.insert(item) - def insert(self, item, start=None, end=None): - if not isinstance(item, RangeMap.Item): - item = RangeMap.Item(obj=item, start=start, end=end) + def insert(self, item, start=None, end=None): + if not isinstance(item, RangeMap.Item): + item = RangeMap.Item(obj=item, start=start, end=end) - self.insert_right(item) + self.insert_right(item) - def find(self, i): - # Finds object for which i falls in the range of that object - idx = bisect.bisect_right(self.keys, i) + def find(self, i): + # Finds object for which i falls in the range of that object + idx = bisect.bisect_right(self.keys, i) - # Go back up until falls within end - while idx > 0: - idx -= 1 + # Go back up until falls within end + while idx > 0: + idx -= 1 - o = self[idx] + o = self[idx] - if i <= o.end: - return o.obj + if i <= o.end: + return o.obj - return None + return None class CommentsDatabase(object): - cldoc_instrre = re.compile('^cldoc:([a-zA-Z_-]+)(\(([^\)]*)\))?') + cldoc_instrre = re.compile('^cldoc:([a-zA-Z_-]+)(\(([^\)]*)\))?') - def __init__(self, filename, tu, opts): - self.filename = filename + def __init__(self, filename, tu, opts): + self.filename = filename - self.categories = RangeMap() - self.comments = Sorted(key=lambda x: x.location.offset) - self.options=opts - self.extract(filename, tu) + self.categories = RangeMap() + self.comments = Sorted(key=lambda x: x.location.offset) + self.options=opts + self.extract(filename, tu) - def parse_cldoc_instruction(self, token, s): - m = CommentsDatabase.cldoc_instrre.match(s) + def parse_cldoc_instruction(self, token, s): + m = CommentsDatabase.cldoc_instrre.match(s) - if not m: - return False + if not m: + return False - func = m.group(1) - args = m.group(3) + func = m.group(1) + args = m.group(3) - if args: - args = [x.strip() for x in args.split(",")] - else: - args = [] + if args: + args = [x.strip() for x in args.split(",")] + else: + args = [] - name = 'cldoc_instruction_{0}'.format(func.replace('-', '_')) + name = 'cldoc_instruction_{0}'.format(func.replace('-', '_')) - if hasattr(self, name): - getattr(self, name)(token, args) - else: - sys.stderr.write('Invalid cldoc instruction: {0}\n'.format(func)) - sys.exit(1) + if hasattr(self, name): + getattr(self, name)(token, args) + else: + sys.stderr.write('Invalid cldoc instruction: {0}\n'.format(func)) + sys.exit(1) - return True + return True - @property - def category_names(self): - for item in self.categories: - yield item.obj + @property + def category_names(self): + for item in self.categories: + yield item.obj - def location_to_str(self, loc): - return '{0}:{1}:{2}'.format(loc.file.name, loc.line, loc.column) + def location_to_str(self, loc): + return '{0}:{1}:{2}'.format(loc.file.name, loc.line, loc.column) - def cldoc_instruction_begin_category(self, token, args): - if len(args) != 1: - sys.stderr.write('No category name specified (at {0})\n'.format(self.location_to_str(token.location))) + def cldoc_instruction_begin_category(self, token, args): + if len(args) != 1: + sys.stderr.write('No category name specified (at {0})\n'.format(self.location_to_str(token.location))) - sys.exit(1) + sys.exit(1) - category = args[0] - self.categories.push(category, token.location.offset) + category = args[0] + self.categories.push(category, token.location.offset) - def cldoc_instruction_end_category(self, token, args): - if len(self.categories.stack) == 0: - sys.stderr.write('Failed to end cldoc category: no category to end (at {0})\n'.format(self.location_to_str(token.location))) + def cldoc_instruction_end_category(self, token, args): + if len(self.categories.stack) == 0: + sys.stderr.write('Failed to end cldoc category: no category to end (at {0})\n'.format(self.location_to_str(token.location))) - sys.exit(1) + sys.exit(1) - last = self.categories.stack[-1] + last = self.categories.stack[-1] - if len(args) == 1 and last.obj != args[0]: - sys.stderr.write('Failed to end cldoc category: current category is `{0}\', not `{1}\' (at {2})\n'.format(last.obj, args[0], self.location_to_str(token.location))) + if len(args) == 1 and last.obj != args[0]: + sys.stderr.write('Failed to end cldoc category: current category is `{0}\', not `{1}\' (at {2})\n'.format(last.obj, args[0], self.location_to_str(token.location))) - sys.exit(1) + sys.exit(1) - self.categories.pop(token.extent.end.offset) + self.categories.pop(token.extent.end.offset) - def lookup_category(self, location): - if location.file.name != self.filename: - return None + def lookup_category(self, location): + if location.file.name != self.filename: + return None - return self.categories.find(location.offset) + return self.categories.find(location.offset) - def lookup(self, location): - if location.file.name != self.filename: - return None + def lookup(self, location): + if location.file.name != self.filename: + return None - return self.comments.find(location.offset) + return self.comments.find(location.offset) - def extract(self, filename, tu): - """ - extract extracts comments from a translation unit for a given file by - iterating over all the tokens in the TU, locating the COMMENT tokens and - finding out to which cursors the comments semantically belong. - """ - it = tu.get_tokens(extent=tu.get_extent(filename, (0, int(os.stat(filename).st_size)))) + def extract(self, filename, tu): + """ + extract extracts comments from a translation unit for a given file by + iterating over all the tokens in the TU, locating the COMMENT tokens and + finding out to which cursors the comments semantically belong. + """ + it = tu.get_tokens(extent=tu.get_extent(filename, (0, int(os.stat(filename).st_size)))) - while True: - try: - self.extract_loop(it) - except StopIteration: - break + while True: + try: + self.extract_loop(it) + except StopIteration: + break - def extract_one(self, token, s): - # Parse special cldoc:() comments for instructions - if self.parse_cldoc_instruction(token, s.strip()): - return + def extract_one(self, token, s): + # Parse special cldoc:() comments for instructions + if self.parse_cldoc_instruction(token, s.strip()): + return - comment = Comment(s, token.location,self.options) - self.comments.insert(comment) + comment = Comment(s, token.location,self.options) + self.comments.insert(comment) - def extract_loop(self, iter): - token = iter.next() + def extract_loop(self, iter): + token = iter.next() - # Skip until comment found - while token.kind != cindex.TokenKind.COMMENT: - token = iter.next() + # Skip until comment found + while token.kind != cindex.TokenKind.COMMENT: + token = iter.next() - comments = [] - prev = None + comments = [] + prev = None - # Concatenate individual comments together, but only if they are strictly - # adjacent - while token.kind == cindex.TokenKind.COMMENT: - cleaned = self.clean(token) + # Concatenate individual comments together, but only if they are strictly + # adjacent + while token.kind == cindex.TokenKind.COMMENT: + cleaned = self.clean(token) - # Process instructions directly, now - if (not cleaned is None) and (not CommentsDatabase.cldoc_instrre.match(cleaned) is None): - comments = [cleaned] - break + # Process instructions directly, now + if (not cleaned is None) and (not CommentsDatabase.cldoc_instrre.match(cleaned) is None): + comments = [cleaned] + break - # Check adjacency - if not prev is None and prev.extent.end.line + 1 < token.extent.start.line: - # Empty previous comment - comments = [] + # Check adjacency + if not prev is None and prev.extent.end.line + 1 < token.extent.start.line: + # Empty previous comment + comments = [] - if not cleaned is None: - comments.append(cleaned) + if not cleaned is None: + comments.append(cleaned) - prev = token - token = iter.next() + prev = token + token = iter.next() - if len(comments) > 0: - self.extract_one(token, "\n".join(comments)) + if len(comments) > 0: + self.extract_one(token, "\n".join(comments)) - def clean(self, token): - prelen = token.extent.start.column - 1 - comment = token.spelling.strip() - - if comment.startswith('///') or comment.startswith('//!'): - return comment[3:].strip() - elif comment.startswith('//'): - # For our purposes, ordinary comments are ignored. - return None - #if len(comment) > 2 and comment[2] == '-': - # return None + def clean(self, token): + prelen = token.extent.start.column - 1 + comment = token.spelling.strip() + + if comment.startswith('///') or comment.startswith('//!'): + return comment[3:].strip() + elif comment.startswith('//'): + # For our purposes, ordinary comments are ignored. + return None + #if len(comment) > 2 and comment[2] == '-': + # return None - return comment[2:].strip() - elif comment.startswith('/*') and comment.endswith('*/'): - # For our purposes, ! is required here. - if comment[2] != '!': - return None + return comment[2:].strip() + elif comment.startswith('/*') and comment.endswith('*/'): + # For our purposes, ! is required here. + if comment[2] != '!': + return None - lines = comment[3:-2].splitlines() + lines = comment[3:-2].splitlines() - if len(lines) == 1 and len(lines[0]) > 0 and lines[0][0] == ' ': - return lines[0][1:].rstrip() + if len(lines) == 1 and len(lines[0]) > 0 and lines[0][0] == ' ': + return lines[0][1:].rstrip() - retl = [] + retl = [] - for line in lines: - if prelen == 0 or line[0:prelen].isspace(): - line = line[prelen:].rstrip() + for line in lines: + if prelen == 0 or line[0:prelen].isspace(): + line = line[prelen:].rstrip() - if line.startswith(' *') or line.startswith(' '): - line = line[2:] + if line.startswith(' *') or line.startswith(' '): + line = line[2:] - if len(line) > 0 and line[0] == ' ': - line = line[1:] + if len(line) > 0 and line[0] == ' ': + line = line[1:] - retl.append(line) + retl.append(line) - return "\n".join(retl) - else: - return comment + return "\n".join(retl) + else: + return comment from pyparsing import * class Parser: - ParserElement.setDefaultWhitespaceChars(' \t\r') - - #All variables defined on the class level in Python are considered static. - # Here, we define static members of the class Parser, from pyparsing: - identifier = Word(alphas + '_', alphanums + '_') - - # I have modified the parser to make brief the default return, and body is optional. Only if there is a double lineEnd will there be a body, - # and it is everything from the first such double lineEnd to the end of the text. - briefline = NotAny('@') + ((Regex('[^\n]+') + lineEnd)) - brief = ZeroOrMore(lineEnd) + Combine(OneOrMore(briefline)).setResultsName('brief') - - paramdesc = restOfLine + ZeroOrMore(lineEnd + ~('@' | lineEnd) + Regex('[^\n]+')) + lineEnd.suppress() - param = '@' + identifier.setResultsName('name') + White() + Combine(paramdesc).setResultsName('description') - - preparams = ZeroOrMore(param.setResultsName('preparam', listAllMatches=True)) - postparams = ZeroOrMore(param.setResultsName('postparam', listAllMatches=True)) - - bodyline = NotAny('@') + (lineEnd | (Regex('[^\n]+') + lineEnd)) - body = OneOrMore(lineEnd) + Combine(ZeroOrMore(bodyline)).setResultsName('body') - - doc = Optional(brief) + preparams + Optional(body) + postparams - - @staticmethod - def parse(s): - return Parser.doc.parseString(s) + ParserElement.setDefaultWhitespaceChars(' \t\r') + + #All variables defined on the class level in Python are considered static. + # Here, we define static members of the class Parser, from pyparsing: + identifier = Word(alphas + '_', alphanums + '_') + + # I have modified the parser to make brief the default return, and body is optional. Only if there is a double lineEnd will there be a body, + # and it is everything from the first such double lineEnd to the end of the text. + briefline = NotAny('@') + ((Regex('[^\n]+') + lineEnd)) + brief = ZeroOrMore(lineEnd) + Combine(Optional(briefline)).setResultsName('brief') + + paramdesc = restOfLine + ZeroOrMore(lineEnd + ~('@' | lineEnd) + Regex('[^\n]+')) + lineEnd.suppress() + param = '@' + identifier.setResultsName('name') + White() + Combine(paramdesc).setResultsName('description') + + preparams = ZeroOrMore(param.setResultsName('preparam', listAllMatches=True)) + postparams = ZeroOrMore(param.setResultsName('postparam', listAllMatches=True)) + + bodyline = NotAny('@') + (lineEnd | (Regex('[^\n]+') + lineEnd)) + body = OneOrMore(lineEnd) + Combine(ZeroOrMore(bodyline)).setResultsName('body') + + doc = Optional(brief) + preparams + Optional(body) + postparams + + @staticmethod + def parse(s): + return Parser.doc.parseString(s) # vi:ts=4:et diff --git a/cldoc/defdict.py b/cldoc/defdict.py index 0b75994..7bd61c7 100644 --- a/cldoc/defdict.py +++ b/cldoc/defdict.py @@ -11,7 +11,7 @@ # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. class Defdict(dict): - def __missing__(self, key): - return None + def __missing__(self, key): + return None # vi:ts=4:et diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index d60fc42..75f2edb 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -7,183 +7,176 @@ from . import fs class DocumentMerger: - reinclude = re.compile('#') - reheading = re.compile('(.*)\\s*{#(?:([0-9]*):)?(.*)}') - def merge(self, mfilter, files): - for f in files: - if os.path.basename(f).startswith('.'): - continue - - if os.path.isdir(f): - self.merge(mfilter, [os.path.join(f, x) for x in os.listdir(f)]) - elif f.endswith('.md'): - self._merge_file(mfilter, f) - - def _split_categories(self, filename, contents): - lines = contents.splitlines() - - ret = {} - title={} - category = None - doc = [] - first = False - ordered = [] - weight = {} - this_weight=0 - - for line in lines: - prefix = '#'): - if len(doc) > 0 and not category: - sys.stderr.write('Failed to merge file `{0}\': no # specified\n'.format(filename)) - sys.exit(1) - - if category: - if not category in ret: - ordered.append(category) - - ret[category] = "\n".join(doc) - - doc = [] - category = line[len(prefix):-1] - this_title=category.strip() - first = True - elif heading: - if heading.group(2): - this_weight=int(heading.group(2)) - category=heading.group(3) - this_title=heading.group(1).strip() - line=this_title - doc.append(line) - else: - doc.append(line) - if not this_weight: - this_weight=0 - if not category and len(doc) > 0: - parts=filename.replace('\\','/').replace('.md','').split('/') - category=parts[len(parts)-1] - this_title=category - - if category: - if not category in ret: - ordered.append(category) - title[category]=this_title - ret[category] = "\n".join(doc) - weight[category] = this_weight - - return [[c, ret[c],title[c],weight[c]] for c in ordered] - - def _normalized_qid(self, qid): - if qid == 'ref': - return None - - if qid.startswith('::'): - return qid[2:] - - return qid - - def _do_include(self, mfilter, filename, relpath): - if not os.path.isabs(relpath): - relpath = os.path.join(os.path.dirname(filename), relpath) - - return self._read_merge_file(mfilter, relpath) - - def _process_includes(self, mfilter, filename, contents): - def repl(m): - return self._do_include(mfilter, filename, m.group(1)) - - return DocumentMerger.reinclude.sub(repl, contents) - - def _read_merge_file(self, mfilter, filename): - if not mfilter is None: - contents = unicode(subprocess.check_output([mfilter, filename]), 'utf-8') - else: - contents = unicode(fs.fs.open(filename).read(), 'utf-8') - - return self._process_includes(mfilter, filename, contents) - - def _merge_file(self, mfilter, filename): - contents = self._read_merge_file(mfilter, filename) - categories = self._split_categories(filename, contents) - - for (category, docstr, cat_title, weight) in categories: - # First, split off any order number from the front e.g. 3:name - category=category.replace('::','_DOUBLECOLONSEPARATOR_') - front_back= category.split(':') - front='' - if len(front_back)>1: - category=front_back[1] - front=front_back[0] - else: - category=front_back[0] - category=category.replace('_DOUBLECOLONSEPARATOR_','::') - parts = category.split('/') - - qid = self._normalized_qid(parts[0]) - key = 'doc' - - #if len(parts) > 1: - # key = parts[1] - # 'ref' means the root of the reference: - if qid=='ref': - qid=None - if not self.qid_to_node[qid]: - self.add_categories([[qid,cat_title]]) - node = self.category_to_node[qid] - else: - node = self.qid_to_node[qid] - if qid==None: - node.set_title(cat_title) - node.weight=weight - if key == 'doc': - node.merge_comment(comment.Comment(docstr, filename, self.options), override=True) - else: - sys.stderr.write('Unknown type `{0}\' for id `{1}\'\n'.format(key, parts[0])) - sys.exit(1) - - def add_categories(self, categories): - root = None - - for category,title in categories: - parts = category.split('::') - - root = self.root - fullname = '' - - for i in range(len(parts)): - part = parts[i] - found = False - - if i != 0: - fullname += '::' - - fullname += part - - for child in root.children: - if isinstance(child, nodes.Category) and child.name == part: - root = child - found = True - break - - if not found: - s = nodes.Category(part,title) - - root.append(s) - root = s - - self.category_to_node[fullname] = s - self.qid_to_node[s.qid] = s - self.all_nodes.append(s) - - return root + reinclude = re.compile('#') + reheading = re.compile('(.*)\\s*{#(?:([0-9]*):)?(.*)}') + def merge(self, mfilter, files): + for f in files: + if os.path.basename(f).startswith('.'): + continue + + if os.path.isdir(f): + self.merge(mfilter, [os.path.join(f, x) for x in os.listdir(f)]) + elif f.endswith('.md'): + self._merge_file(mfilter, f) + + def _split_categories(self, filename, contents): + lines = contents.splitlines() + + ret = {} + title={} + category = None + doc = [] + first = False + ordered = [] + weight = {} + this_weight=0 + + for line in lines: + prefix = '#'): + if len(doc) > 0 and not category: + sys.stderr.write('Failed to merge file `{0}\': no # specified\n'.format(filename)) + sys.exit(1) + + if category: + if not category in ret: + ordered.append(category) + + ret[category] = "\n".join(doc) + + doc = [] + category = line[len(prefix):-1] + this_title=category.strip() + first = True + elif heading: + if heading.group(2): + this_weight=int(heading.group(2)) + category=heading.group(3) + this_title=heading.group(1).strip() + line=this_title + #doc.append(line) + else: + doc.append(line) + if not this_weight: + this_weight=0 + if not category and len(doc) > 0: + parts=filename.replace('\\','/').replace('.md','').split('/') + category=parts[len(parts)-1] + this_title=category + + if category: + if not category in ret: + ordered.append(category) + title[category]=this_title + ret[category] = "\n".join(doc) + weight[category] = this_weight + + return [[c, ret[c],title[c],weight[c]] for c in ordered] + + def _normalized_qid(self, qid): + if qid == 'ref': + return None + + if qid.startswith('::'): + return qid[2:] + + return qid + + def _do_include(self, mfilter, filename, relpath): + if not os.path.isabs(relpath): + relpath = os.path.join(os.path.dirname(filename), relpath) + + return self._read_merge_file(mfilter, relpath) + + def _process_includes(self, mfilter, filename, contents): + def repl(m): + return self._do_include(mfilter, filename, m.group(1)) + + return DocumentMerger.reinclude.sub(repl, contents) + + def _read_merge_file(self, mfilter, filename): + if not mfilter is None: + contents = unicode(subprocess.check_output([mfilter, filename]), 'utf-8') + else: + contents = unicode(fs.fs.open(filename).read(), 'utf-8') + + return self._process_includes(mfilter, filename, contents) + + def _merge_file(self, mfilter, filename): + contents = self._read_merge_file(mfilter, filename) + categories = self._split_categories(filename, contents) + + for (category, docstr, cat_title, weight) in categories: + # First, split off any order number from the front e.g. 3:name + category=category.replace('::','_DOUBLECOLONSEPARATOR_') + front_back= category.split(':') + front='' + if len(front_back)>1: + category=front_back[1] + front=front_back[0] + else: + category=front_back[0] + category=category.replace('_DOUBLECOLONSEPARATOR_','::') + parts = category.split('/') + + qid = self._normalized_qid(parts[0]) + + # 'ref' means the root of the reference: + if qid=='ref': + qid=None + if not self.qid_to_node[qid]: + self.add_categories([[qid,cat_title]]) + node = self.category_to_node[qid] + else: + node = self.qid_to_node[qid] + if qid==None: + node.set_title(cat_title) + node.weight=weight + node.merge_comment(comment.Comment(docstr, filename, self.options), override=True) + + def add_categories(self, categories): + root = None + + for category,title in categories: + parts = category.split('::') + + root = self.root + fullname = '' + + for i in range(len(parts)): + part = parts[i] + found = False + + if i != 0: + fullname += '::' + + fullname += part + + for child in root.children: + if isinstance(child, nodes.Category) and child.name == part: + root = child + found = True + break + + if not found: + s = nodes.Category(part,title) + + root.append(s) + root = s + + self.category_to_node[fullname] = s + self.qid_to_node[s.qid] = s + self.all_nodes.append(s) + + return root # vi:ts=4:et diff --git a/cldoc/example.py b/cldoc/example.py index 692e629..20f90b0 100644 --- a/cldoc/example.py +++ b/cldoc/example.py @@ -15,12 +15,12 @@ from . import utf8 class Example(list): - Item = Struct.define('Item', text='', classes=None) + Item = Struct.define('Item', text='', classes=None) - def append(self, text, classes=None): - if isinstance(classes, utf8.string): - classes = [classes] + def append(self, text, classes=None): + if isinstance(classes, utf8.string): + classes = [classes] - list.append(self, Example.Item(text=text, classes=classes)) + list.append(self, Example.Item(text=text, classes=classes)) # vi:ts=4:et diff --git a/cldoc/fs.py b/cldoc/fs.py index 95b9579..6b7d653 100644 --- a/cldoc/fs.py +++ b/cldoc/fs.py @@ -17,102 +17,102 @@ from StringIO import StringIO class System: - @staticmethod - def open(*args): - return open(*args) + @staticmethod + def open(*args): + return open(*args) - @staticmethod - def makedirs(*args): - return os.makedirs(*args) + @staticmethod + def makedirs(*args): + return os.makedirs(*args) - @staticmethod - def mkdtemp(): - return tempfile.mkdtemp() + @staticmethod + def mkdtemp(): + return tempfile.mkdtemp() - @staticmethod - def clear(): - pass + @staticmethod + def clear(): + pass - @staticmethod - def copytree(*args): - shutil.copytree(*args) + @staticmethod + def copytree(*args): + shutil.copytree(*args) - @staticmethod - def rmtree(*args): - shutil.rmtree(*args) + @staticmethod + def rmtree(*args): + shutil.rmtree(*args) class Virtual: - class NeverCloseIO(StringIO): - def close(self): - self.seek(0) + class NeverCloseIO(StringIO): + def close(self): + self.seek(0) - def __exit__(self, type, value, traceback): - self.close() + def __exit__(self, type, value, traceback): + self.close() - def __enter__(self): - return self + def __enter__(self): + return self - @property - def value(self): - data = self.getvalue() - self.seek(0) + @property + def value(self): + data = self.getvalue() + self.seek(0) - return data + return data - files = {} + files = {} - @staticmethod - def open(*args): - fname = args[0] + @staticmethod + def open(*args): + fname = args[0] - if not os.path.isabs(fname): - fname = os.path.join(os.getcwd(), fname) + if not os.path.isabs(fname): + fname = os.path.join(os.getcwd(), fname) - if len(args) != 1 and 'w' in args[1]: - ret = Virtual.NeverCloseIO() + if len(args) != 1 and 'w' in args[1]: + ret = Virtual.NeverCloseIO() - Virtual.files[fname] = ret - return ret - elif fname in Virtual.files: - return Virtual.NeverCloseIO(Virtual.files[fname].getvalue()) - else: - ret = Virtual.NeverCloseIO(open(*args).read().decode('utf-8')) - Virtual.files[fname] = ret + Virtual.files[fname] = ret + return ret + elif fname in Virtual.files: + return Virtual.NeverCloseIO(Virtual.files[fname].getvalue()) + else: + ret = Virtual.NeverCloseIO(open(*args).read().decode('utf-8')) + Virtual.files[fname] = ret - return ret + return ret - @staticmethod - def makedirs(*args): - pass + @staticmethod + def makedirs(*args): + pass - @staticmethod - def mkdtemp(): - def randname(n): - alpha = "abcdefhijklmnopqrstuvwxyz" - ret = "" + @staticmethod + def mkdtemp(): + def randname(n): + alpha = "abcdefhijklmnopqrstuvwxyz" + ret = "" - for i in range(n): - ret += alpha[random.randrange(0, len(alpha))] + for i in range(n): + ret += alpha[random.randrange(0, len(alpha))] - return ret + return ret - while True: - n = os.path.join("/", "tmp", randname(8)) + while True: + n = os.path.join("/", "tmp", randname(8)) - if not n in Virtual.files: - return n + if not n in Virtual.files: + return n - @staticmethod - def clear(): - Virtual.files = {} + @staticmethod + def clear(): + Virtual.files = {} - @staticmethod - def copytree(*args): - pass + @staticmethod + def copytree(*args): + pass - @staticmethod - def rmtree(*args): - pass + @staticmethod + def rmtree(*args): + pass fs = System diff --git a/cldoc/generators/generator.py b/cldoc/generators/generator.py index fd5a216..615ef09 100644 --- a/cldoc/generators/generator.py +++ b/cldoc/generators/generator.py @@ -11,20 +11,20 @@ # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. class Generator(object): - def __init__(self, tree=None, opts=None): - self.tree = tree - self.options = opts + def __init__(self, tree=None, opts=None): + self.tree = tree + self.options = opts - def generate(self, outdir): - self.outdir = outdir - - self.generate_node(self.tree.root) - for node in self.tree.root.sorted_children(): - self.generate_node(node) + def generate(self, outdir): + self.outdir = outdir + + self.generate_node(self.tree.root) + for node in self.tree.root.sorted_children(): + self.generate_node(node) - def generate_node(self, node, passfunc=None): - for child in node.sorted_children(): - if passfunc is None or passfunc(child): - self.generate_node(child) + def generate_node(self, node, passfunc=None): + for child in node.sorted_children(): + if passfunc is None or passfunc(child): + self.generate_node(child) # vi:ts=4:et diff --git a/cldoc/generators/html.py b/cldoc/generators/html.py index 79fbe3c..8b82852 100644 --- a/cldoc/generators/html.py +++ b/cldoc/generators/html.py @@ -20,63 +20,63 @@ from cldoc import fs class Html(Generator): - def generate(self, output, isstatic, customjs=[], customcss=[]): - # Write out json document for search - self.write_search(output) + def generate(self, output, isstatic, customjs=[], customcss=[]): + # Write out json document for search + self.write_search(output) - d = os.path.dirname(__file__) + d = os.path.dirname(__file__) - datadir = os.path.abspath(os.path.join(d, '..', 'data')) - index = os.path.join(datadir, 'index.html') + datadir = os.path.abspath(os.path.join(d, '..', 'data')) + index = os.path.join(datadir, 'index.html') - try: - fs.fs.makedirs(datadir) - except: - pass + try: + fs.fs.makedirs(datadir) + except: + pass - outfile = os.path.join(output, 'index.html') + outfile = os.path.join(output, 'index.html') - jstags = [''.format(x) for x in customjs] - csstags = [''.format(x) for x in customcss] + jstags = [''.format(x) for x in customjs] + csstags = [''.format(x) for x in customcss] - with fs.fs.open(index) as f: - content = f.read() + with fs.fs.open(index) as f: + content = f.read() - templ = '' - content = content.replace(templ, " ".join(jstags)) + templ = '' + content = content.replace(templ, " ".join(jstags)) - templ = '' - content = content.replace(templ, " ".join(csstags)) + templ = '' + content = content.replace(templ, " ".join(csstags)) - with fs.fs.open(outfile, 'w') as o: - o.write(content) - - Generator.generate(self, outdir) - if "CLDOC_DEV" in os.environ: - fs.fs.rmtree(os.path.join(output, "javascript"), True) - fs.fs.copytree(os.path.join(datadir, "javascript"), os.path.join(output, "javascript")) + with fs.fs.open(outfile, 'w') as o: + o.write(content) + + Generator.generate(self, outdir) + if "CLDOC_DEV" in os.environ: + fs.fs.rmtree(os.path.join(output, "javascript"), True) + fs.fs.copytree(os.path.join(datadir, "javascript"), os.path.join(output, "javascript")) - fs.fs.rmtree(os.path.join(output, "styles"), True) - fs.fs.copytree(os.path.join(datadir, "styles"), os.path.join(output, "styles")) + fs.fs.rmtree(os.path.join(output, "styles"), True) + fs.fs.copytree(os.path.join(datadir, "styles"), os.path.join(output, "styles")) - print('Generated `{0}\''.format(outfile)) + print('Generated `{0}\''.format(outfile)) - def write_search(self, output): - search = Search(self.tree) + def write_search(self, output): + search = Search(self.tree) - records = [None] * len(search.records) + records = [None] * len(search.records) - for r in range(len(search.records)): - rec = search.records[r] + for r in range(len(search.records)): + rec = search.records[r] - records[r] = ( - rec.s, - rec.node.refid, - ) + records[r] = ( + rec.s, + rec.node.refid, + ) - outfile = os.path.join(output, 'search.json') + outfile = os.path.join(output, 'search.json') - with fs.fs.open(outfile, 'w') as f: - f.write(json.dumps({'records': records, 'suffixes': search.db})) + with fs.fs.open(outfile, 'w') as f: + f.write(json.dumps({'records': records, 'suffixes': search.db})) # vi:ts=4:et diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index 0c8a3a8..af73fb8 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -24,926 +24,927 @@ from cldoc import fs class Md(Generator): - def __init__(self, tree=None, opts=None): - self.tree = tree - self.options = opts - self.namespaces_as_directories=True - self._refid=None + def __init__(self, tree=None, opts=None): + self.tree = tree + self.options = opts + self.namespaces_as_directories=True + self._refid=None - def generate(self, outdir): - if not outdir: - outdir = '' + def generate(self, outdir): + if not outdir: + outdir = '' - try: - fs.fs.makedirs(outdir) - except OSError: - pass + try: + fs.fs.makedirs(outdir) + except OSError: + pass - ElementTree.register_namespace('gobject', 'http://jessevdk.github.com/cldoc/gobject/1.0') - ElementTree.register_namespace('cldoc', 'http://jessevdk.github.com/cldoc/1.0') - - self.index = ElementTree.Element('index') - self.written = {} - - self.indexmap = { - self.tree.root: self.index - } - - cm = self.tree.root.comment - - if cm: - if cm.brief: - self.index.append(self.doc_to_md(self.tree.root, cm.brief, 'brief')) - - if cm.doc: - self.index.append(self.doc_to_md(self.tree.root, cm.doc)) - - Generator.generate(self, outdir) - - if self.options.report: - self.add_report() - - #self.write_md(self.index, 'index.md') - - print('Generated `{0}\''.format(outdir)) - - - def add_report(self): - from .report import Report - - reportname = 'report' - - while reportname + '.md' in self.written: - reportname = '_' + reportname - - page = Report(self.tree, self.options).generate(reportname) - - elem = ElementTree.Element('report') - elem.set('name', 'Documentation generator') - elem.set('ref', reportname) - - self.index.append(elem) - - self.write_md(page, reportname + '.md') - - def indent(self, elem, level=0): - i = "\n" + " " * level - - if elem.tag == 'doc': - return - - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - - for e in elem: - self.indent(e, level + 1) - - if not e.tail or not e.tail.strip(): - e.tail = i + " " - if not e.tail or not e.tail.strip(): - e.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - - def ref_to_link(self,rf): - parts=rf.split('#') - if len(parts)==2: - link=parts[1] - link=link.replace('::',self.namespace_separator) - fullpath=os.path.join(self.outdir, link) - # Now this link might contain a path, or the current page might have a path. - relpath=os.path.relpath(fullpath,self.current_path).replace('\\','/') - link=relpath - else: - link='' - return link - - def link_md(self,title, rf): - lnk=self.ref_to_link(rf) - return '['+title+']('+lnk+')' - - def list_bases(self,f,elem): - for child in elem.getchildren(): - self.indent(child) - if child.tag=='type': - title=child.attrib['name'] - ref='' - if child.attrib.has_key('ref'): - ref=child.attrib['ref'] - f.write(self.link_md(title,ref)) - f.write('\n') - - def get_return_type(self,elem): - ret_parts=[] - for child in elem.getchildren(): - if child.tag=='return': - for c in child.getchildren(): - if c.tag=='type': - ret_parts.append(c.attrib['name']) - if c.attrib.has_key('qualifier'): - ret_parts.append(c.attrib['qualifier']); - ret_type=' '.join(ret_parts); - return ret_type - - def get_brief(self,elem): - brief='' - for child in elem.getchildren(): - if child.tag=='brief': - brief=child.text - # can't have newlines without breaking the table structure. - brief=brief.replace('\n','
') - return brief - - def get_location(self,elem): - location='' - if elem.attrib.has_key('location'): - return elem.attrib['location'] - return location - def get_lib(self,elem): - lib='' - if elem.attrib.has_key('lib'): - return elem.attrib['lib'] - return lib - - def get_doc(self,elem): - doc='' - for child in elem.getchildren(): - if child.tag=='doc': - doc=self.process_elem(child) - return doc - - def return_type(self,elem): - ret_type=dict() - for child in elem.getchildren(): - if child.tag=='type': - ret_type['type']=child.attrib['name'] - return ret_type - - def argument(self,elem): - ret_arg=dict() - ret_arg['name']=elem.attrib['name'] - for child in elem.getchildren(): - if child.tag=='type': - if child.attrib.has_key('name'): - ret_arg['type']=child.attrib['name'] - else: - ret_arg['type']='' - return ret_arg - - def get_arguments(self,elem): - arguments=[] - for child in elem.getchildren(): - if child.tag=='argument': - arguments.append(self.argument(child)) - return arguments - - def get_arguments_text(self,elem): - arguments=self.get_arguments(elem) - args_txt=[] - for arg in arguments: - args_txt.append(arg['name']) - arglist=','.join(args_txt) - return arglist - - def get_typed_arguments_text(self,elem): - arguments=self.get_arguments(elem) - args_txt=[] - for arg in arguments: - tp_parts=[] - if arg['type']!='': - tp_parts.append(arg['type']) - if arg['name']!='': - tp_parts.append(arg['name']) - tp=' '.join(tp_parts) - args_txt.append(tp) - arglist=', '.join(args_txt) - return arglist - - def has_doc(self,elem): - for child in elem.getchildren(): - if child.tag=='brief' or child.tag=='doc': - if child.text!='': - return True - if self.has_doc(child): - return True - return False - - def doc_method(self,f,elem): - ret_type='' - doc='' - brief='' - for child in elem.getchildren(): - if child.tag=='return': - ret_type=self.get_return_type(elem) - elif child.tag=='brief': - brief=child.text - elif child.tag=='doc': - doc=child.text - #blank line before a heading h4: - f.write('\n### '+ret_type+' '+elem.attrib['name']) - - arglist=self.get_typed_arguments_text(elem) - f.write('('+arglist+')') - f.write('\n') - if brief!='': - f.write(brief+'\n') - if doc!='': - f.write(doc+'\n') - def get_docs(self,elem): - doc='' - brief='' - for child in elem.getchildren(): - if child.tag=='brief': - brief=child.text - elif child.tag=='doc': - doc=child.text - return brief,doc - - def doc_typedef(self,f,elem): - brief,doc=self.get_docs(elem) - if brief!='' or doc!='': - f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') - - def doc_enum(self,f,elem): - brief,doc=self.get_docs(elem) - if brief!='' or doc!='': - f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') - - def doc_field(self,f,elem): - brief,doc=self.get_docs(elem) - if brief!='' or doc!='': - f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') - - def process_text(self,txt): - return txt - - def process_elem(self,elem): - res=elem.text - for child in elem.getchildren(): - if child.tag=="ref": - title=child.text - if child.attrib.has_key('ref'): - link=self.ref_to_link(child.attrib['ref']) - #res+='[{0}]({1})'.format(title,link) - res+='{0}'.format(title,link) - res+=child.tail - else: - res+=self.process_elem(child) - res+=elem.tail - return res - - def write_md(self, elem, fname): - - self.written[fname] = True - fullpath=os.path.join(self.outdir, fname) - - tree = ElementTree.ElementTree(elem) - - self.indent(tree.getroot()) - if elem.attrib.has_key('title'): - title=elem.attrib['title'] - elif elem.attrib.has_key('name'): - title=elem.attrib['name'] - elif elem.tag=='index': - title='Index' - else: - title='Untitled' - - weight=0 - if elem.attrib.has_key('weight'): - weight=elem.attrib['weight'] - layout_name='reference' - - # if(elem.tag=='category'): - #else: - # fullpath=os.path.join(os.path.join(self.outdir, 'ref'),fname) - - self.current_path='' - try: - head_tail=os.path.split(fullpath) - self.current_path=head_tail[0] - os.makedirs(self.current_path) - except: - pass - location=self.get_location(elem) - print(location+' (0): Documenting '+title) - - f = fs.fs.open(fullpath, 'w') - f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\nweight: '+str(weight)+'\n---\n') - brief=self.get_brief(elem) - doc=self.get_doc(elem) - if(elem.tag=='category'): - #f.write(title) - #f.write('\n===\n\n') - f.write(brief) - f.write(doc) - else: - if elem.tag=='index' or elem.tag=='root': - f.write(title) - else: - f.write(elem.tag+' '+title) - f.write('\n===\n\n') - lib=self.get_lib(elem) - if location and location!='': - f.write('| Include: | '+location+' |\n\n') - if lib and lib!='': - f.write('| Library: | '+lib+' |\n\n') - - if brief=='': - brief=doc - if doc=='': - doc=brief - if brief: - f.write(brief+'\n') - - # method declarations - f.write('\n') - namespaces=[] - classes=[] - methods=[] - bases=[] - fields=[] - typedefs=[] - enums=[] - variables=[] - for child in elem.getchildren(): - if child.tag=='base': - bases.append(child) - elif child.tag=='class' or child.tag=='struct': - if self.has_doc(child): - classes.append(child) - elif child.tag=='method' or child.tag=='function' or child.tag=='constructor' or child.tag=='destructor': - if self.has_doc(child): - methods.append(child) - elif child.tag=='namespace': - if self.has_doc(child): - namespaces.append(child) - elif child.tag=='field': - if self.has_doc(child): - fields.append(child) - elif child.tag=='typedef': - if self.has_doc(child): - typedefs.append(child) - elif child.tag=='enum': - if self.has_doc(child): - enums.append(child) - elif child.tag=='variable': - if self.has_doc(child): - variables.append(child) - elif child.tag=='brief' or child.tag=='doc': - pass - else: - print(child.tag) - - for child in namespaces: - title=child.tag+' '+child.attrib['name'] - ref=child.attrib['ref'] - lnk=self.ref_to_link(ref) - #f.write(self.link_md(title,ref)+'\n') - br=self.get_brief(child).replace('\n','') - f.write('\n| ['+title+']('+lnk+') | '+br+' |') - - # children: - if len(bases): - for child in bases: - self.indent(child) - self.list_bases(f,child) - - if len(classes): - f.write('\nClasses and Structures\n---\n') - for child in classes: - title=child.attrib['name'] - ref=child.attrib['ref'] - if self.has_doc(child): - lnk=self.ref_to_link(ref) - title='['+title+']('+lnk+')' - else: - continue - br=self.get_brief(child) - f.write('\n| '+child.tag+' '+title+' | '+br+' |') - f.write('\n') - - if len(methods): - f.write('\nFunctions\n---\n') - for child in methods: - f.write('\n| '+self.get_return_type(child)+' | ['+child.attrib['name']+'](#'+child.attrib['name']+')('+self.get_typed_arguments_text(child)+') |') - any_methods=True - - f.write('\n') - # main text - if doc: - f.write('\n'+doc+'\n') - f.write('\n') - - if len(bases)>0: - f.write('\nBase Classes\n---\n') - for child in bases: - self.indent(child) - if child.tag=='base': - self.list_bases(f,child) - - if len(methods)>0: - f.write('\nFunctions\n---\n') - for child in methods: - self.indent(child) - self.doc_method(f,child) - - if len(fields)>0: - f.write('\nFields\n---\n') - if len(variables)>0: - f.write('\nVariables\n---\n') - if len(typedefs)>0: - f.write('\nTypedefs\n---\n') - for child in typedefs: - self.indent(child) - self.doc_typedef(f,child) - if len(enums)>0: - f.write('\nEnums\n---\n') - for child in enums: - self.indent(child) - self.doc_enum(f,child) - - for child in fields: - self.indent(child) - self.doc_field(f,child) - #tree.write(f, encoding='utf-8', xml_declaration=True) - - f.close() - - def is_page(self, node): - if node.force_page: - return True - - if isinstance(node, nodes.Struct) and node.is_anonymous: - return False - - if isinstance(node, nodes.Class): - for child in node.children: - if not (isinstance(child, nodes.Field) or \ - isinstance(child, nodes.Variable) or \ - isinstance(child, nodes.TemplateTypeParameter)): - return True - - return False - - pagecls = [nodes.Namespace, nodes.Category, nodes.Root] - - for cls in pagecls: - if isinstance(node, cls): - return True - - if isinstance(node, nodes.Typedef) and len(node.children) > 0: - return True - - return False - - def is_top(self, node): - if self.is_page(node): - return True - - if node.parent == self.tree.root: - return True - - return False - - def refid(self, node): - try: - if not node._refid is None: - return node._refid - except: - return '' - - parent = node - - meid = node.qid - - if not node.parent or (isinstance(node.parent, nodes.Root) and not self.is_page(node)): - return 'index#' + meid - - # Find topmost parent - while not self.is_page(parent): - parent = parent.parent - - if not node is None: - node._refid = parent.qid + '#' + meid - return node._refid - else: - return None + ElementTree.register_namespace('gobject', 'http://jessevdk.github.com/cldoc/gobject/1.0') + ElementTree.register_namespace('cldoc', 'http://jessevdk.github.com/cldoc/1.0') + + self.index = ElementTree.Element('index') + self.written = {} + + self.indexmap = { + self.tree.root: self.index + } + + cm = self.tree.root.comment + + if cm: + if cm.brief: + self.index.append(self.doc_to_md(self.tree.root, cm.brief, 'brief')) + + if cm.doc: + self.index.append(self.doc_to_md(self.tree.root, cm.doc)) + + Generator.generate(self, outdir) + + if self.options.report: + self.add_report() + + #self.write_md(self.index, 'index.md') + + print('Generated `{0}\''.format(outdir)) + + + def add_report(self): + from .report import Report + + reportname = 'report' + + while reportname + '.md' in self.written: + reportname = '_' + reportname + + page = Report(self.tree, self.options).generate(reportname) + + elem = ElementTree.Element('report') + elem.set('name', 'Documentation generator') + elem.set('ref', reportname) + + self.index.append(elem) + + self.write_md(page, reportname + '.md') + + def indent(self, elem, level=0): + i = "\n" + " " * level + + if elem.tag == 'doc': + return + + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + + for e in elem: + self.indent(e, level + 1) + + if not e.tail or not e.tail.strip(): + e.tail = i + " " + if not e.tail or not e.tail.strip(): + e.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def ref_to_link(self,rf): + parts=rf.split('#') + if len(parts)==2: + link=parts[1] + link=link.replace('::',self.namespace_separator) + fullpath=os.path.join(self.outdir, link) + # Now this link might contain a path, or the current page might have a path. + relpath=os.path.relpath(fullpath,self.current_path).replace('\\','/') + link=relpath + else: + link='' + return link + + def link_md(self,title, rf): + lnk=self.ref_to_link(rf) + return '['+title+']('+lnk+')' + + def list_bases(self,f,elem): + for child in elem.getchildren(): + self.indent(child) + if child.tag=='type': + title=child.attrib['name'] + ref='' + if child.attrib.has_key('ref'): + ref=child.attrib['ref'] + f.write(self.link_md(title,ref)) + f.write('\n') + + def get_return_type(self,elem): + ret_parts=[] + for child in elem.getchildren(): + if child.tag=='return': + for c in child.getchildren(): + if c.tag=='type': + ret_parts.append(c.attrib['name']) + if c.attrib.has_key('qualifier'): + ret_parts.append(c.attrib['qualifier']); + ret_type=' '.join(ret_parts); + return ret_type + + def get_brief(self,elem): + brief='' + for child in elem.getchildren(): + if child.tag=='brief': + brief=child.text + # can't have newlines without breaking the table structure. + brief=brief.replace('\n','
') + return brief + + def get_location(self,elem): + location='' + if elem.attrib.has_key('location'): + return elem.attrib['location'] + return location + def get_lib(self,elem): + lib='' + if elem.attrib.has_key('lib'): + return elem.attrib['lib'] + return lib + + def get_doc(self,elem): + doc='' + for child in elem.getchildren(): + if child.tag=='doc': + doc=self.process_elem(child) + return doc + + def return_type(self,elem): + ret_type=dict() + for child in elem.getchildren(): + if child.tag=='type': + ret_type['type']=child.attrib['name'] + return ret_type + + def argument(self,elem): + ret_arg=dict() + ret_arg['name']=elem.attrib['name'] + for child in elem.getchildren(): + if child.tag=='type': + if child.attrib.has_key('name'): + ret_arg['type']=child.attrib['name'] + else: + ret_arg['type']='' + return ret_arg + + def get_arguments(self,elem): + arguments=[] + for child in elem.getchildren(): + if child.tag=='argument': + arguments.append(self.argument(child)) + return arguments + + def get_arguments_text(self,elem): + arguments=self.get_arguments(elem) + args_txt=[] + for arg in arguments: + args_txt.append(arg['name']) + arglist=','.join(args_txt) + return arglist + + def get_typed_arguments_text(self,elem): + arguments=self.get_arguments(elem) + args_txt=[] + for arg in arguments: + tp_parts=[] + if arg['type']!='': + tp_parts.append(arg['type']) + if arg['name']!='': + tp_parts.append(arg['name']) + tp=' '.join(tp_parts) + args_txt.append(tp) + arglist=', '.join(args_txt) + return arglist + + def has_doc(self,elem): + for child in elem.getchildren(): + if child.tag=='brief' or child.tag=='doc': + if child.text!='': + return True + if self.has_doc(child): + return True + return False + + def doc_method(self,f,elem): + ret_type='' + doc='' + brief='' + for child in elem.getchildren(): + if child.tag=='return': + ret_type=self.get_return_type(elem) + elif child.tag=='brief': + brief=child.text + elif child.tag=='doc': + doc=child.text + #blank line before a heading h4: + f.write('\n### '+ret_type+' '+elem.attrib['name']) + + arglist=self.get_typed_arguments_text(elem) + f.write('('+arglist+')') + f.write('\n') + if brief!='': + f.write(brief+'\n') + if doc!='': + f.write(doc+'\n') + def get_docs(self,elem): + doc='' + brief='' + for child in elem.getchildren(): + if child.tag=='brief': + brief=child.text + elif child.tag=='doc': + doc=child.text + return brief,doc + + def doc_typedef(self,f,elem): + brief,doc=self.get_docs(elem) + if brief!='' or doc!='': + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + + def doc_enum(self,f,elem): + brief,doc=self.get_docs(elem) + if brief!='' or doc!='': + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + + def doc_field(self,f,elem): + brief,doc=self.get_docs(elem) + if brief!='' or doc!='': + f.write('\n**'+elem.attrib['name']+'** '+brief+' '+doc+'\n') + + def process_text(self,txt): + return txt + + def process_elem(self,elem): + res=elem.text + for child in elem.getchildren(): + if child.tag=="ref": + title=child.text + if child.attrib.has_key('ref'): + link=self.ref_to_link(child.attrib['ref']) + #res+='[{0}]({1})'.format(title,link) + res+='{0}'.format(title,link) + res+=child.tail + else: + res+=self.process_elem(child) + res+=elem.tail + return res + + def write_md(self, elem, fname): + + self.written[fname] = True + fullpath=os.path.join(self.outdir, fname) + + tree = ElementTree.ElementTree(elem) + + self.indent(tree.getroot()) + if elem.attrib.has_key('title'): + title=elem.attrib['title'] + elif elem.attrib.has_key('name'): + title=elem.attrib['name'] + elif elem.tag=='index': + title='Index' + else: + title='Untitled' + + weight=0 + if elem.attrib.has_key('weight'): + weight=elem.attrib['weight'] + layout_name='reference' + + # if(elem.tag=='category'): + #else: + # fullpath=os.path.join(os.path.join(self.outdir, 'ref'),fname) + + self.current_path='' + try: + head_tail=os.path.split(fullpath) + self.current_path=head_tail[0] + os.makedirs(self.current_path) + except: + pass + location=self.get_location(elem) + print(location+' (0): Documenting '+title) + + f = fs.fs.open(fullpath, 'w') + f.write('---\n'+'title: '+title+'\nlayout: '+layout_name+'\nweight: '+str(weight)+'\n---\n') + brief=self.get_brief(elem) + doc=self.get_doc(elem) + if(elem.tag=='category'): + f.write(title) + f.write('\n===\n\n') + f.write(brief) + f.write(doc) + else: + if elem.tag=='index' or elem.tag=='root': + f.write(title) + else: + f.write(elem.tag+' '+title) + f.write('\n===\n\n') + lib=self.get_lib(elem) + if location and location!='': + f.write('| Include: | '+location+' |\n\n') + if lib and lib!='': + f.write('| Library: | '+lib+' |\n\n') + + if brief=='': + brief=doc + if doc=='': + doc=brief + if brief: + f.write(brief+'\n') + + # method declarations + f.write('\n') + namespaces=[] + classes=[] + methods=[] + bases=[] + fields=[] + typedefs=[] + enums=[] + variables=[] + for child in elem.getchildren(): + if child.tag=='base': + bases.append(child) + elif child.tag=='class' or child.tag=='struct': + if self.has_doc(child): + classes.append(child) + elif child.tag=='method' or child.tag=='function' or child.tag=='constructor' or child.tag=='destructor': + if self.has_doc(child): + methods.append(child) + elif child.tag=='namespace': + if self.has_doc(child): + namespaces.append(child) + elif child.tag=='field': + if self.has_doc(child): + fields.append(child) + elif child.tag=='typedef': + if self.has_doc(child): + typedefs.append(child) + elif child.tag=='enum': + if self.has_doc(child): + enums.append(child) + elif child.tag=='variable': + if self.has_doc(child): + variables.append(child) + elif child.tag=='brief' or child.tag=='doc': + pass + else: + print(child.tag) + + for child in namespaces: + title=child.tag+' '+child.attrib['name'] + ref=child.attrib['ref'] + lnk=self.ref_to_link(ref) + #f.write(self.link_md(title,ref)+'\n') + br=self.get_brief(child).replace('\n','') + f.write('\n| ['+title+']('+lnk+') | '+br+' |') + + # children: + if len(bases): + for child in bases: + self.indent(child) + self.list_bases(f,child) + + if len(classes): + f.write('\nClasses and Structures\n---\n') + for child in classes: + title=child.attrib['name'] + ref=child.attrib['ref'] + if self.has_doc(child): + lnk=self.ref_to_link(ref) + title='['+title+']('+lnk+')' + else: + continue + br=self.get_brief(child) + f.write('\n| '+child.tag+' '+title+' | '+br+' |') + f.write('\n') + + if len(methods): + f.write('\nFunctions\n---\n') + for child in methods: + f.write('\n| '+self.get_return_type(child)+' | ['+child.attrib['name']+'](#'+child.attrib['name']+')('+self.get_typed_arguments_text(child)+') |') + any_methods=True + + f.write('\n') + # main text + if doc: + f.write('\n'+doc+'\n') + f.write('\n') + + if len(bases)>0: + f.write('\nBase Classes\n---\n') + for child in bases: + self.indent(child) + if child.tag=='base': + self.list_bases(f,child) + + if len(methods)>0: + f.write('\nFunctions\n---\n') + for child in methods: + self.indent(child) + self.doc_method(f,child) + + if len(fields)>0: + f.write('\nFields\n---\n') + if len(variables)>0: + f.write('\nVariables\n---\n') + if len(typedefs)>0: + f.write('\nTypedefs\n---\n') + for child in typedefs: + self.indent(child) + self.doc_typedef(f,child) + if len(enums)>0: + f.write('\nEnums\n---\n') + for child in enums: + self.indent(child) + self.doc_enum(f,child) + + for child in fields: + self.indent(child) + self.doc_field(f,child) + #tree.write(f, encoding='utf-8', xml_declaration=True) + + f.close() + + def is_page(self, node): + if node.force_page: + return True + + if isinstance(node, nodes.Struct) and node.is_anonymous: + return False + + if isinstance(node, nodes.Class): + for child in node.children: + if not (isinstance(child, nodes.Field) or \ + isinstance(child, nodes.Variable) or \ + isinstance(child, nodes.TemplateTypeParameter)): + return True + + return False + + pagecls = [nodes.Namespace, nodes.Category, nodes.Root] + + for cls in pagecls: + if isinstance(node, cls): + return True + + if isinstance(node, nodes.Typedef) and len(node.children) > 0: + return True + + return False + + def is_top(self, node): + if self.is_page(node): + return True + + if node.parent == self.tree.root: + return True + + return False + + def refid(self, node): + try: + if not node._refid is None: + return node._refid + except: + return '' + + parent = node + + meid = node.qid + + if not node.parent or (isinstance(node.parent, nodes.Root) and not self.is_page(node)): + return 'index#' + meid + + # Find topmost parent + while not self.is_page(parent): + parent = parent.parent + + if not node is None: + node._refid = parent.qid + '#' + meid + return node._refid + else: + return None - def add_ref_node_id(self, node, elem): - r = self.refid(node) + def add_ref_node_id(self, node, elem): + r = self.refid(node) - if not r is None: - elem.set('ref', r) + if not r is None: + elem.set('ref', r) - def add_ref_id(self, cursor, elem): - if not cursor: - return + def add_ref_id(self, cursor, elem): + if not cursor: + return - if cursor in self.tree.cursor_to_node: - node = self.tree.cursor_to_node[cursor] - elif cursor.get_usr() in self.tree.usr_to_node: - node = self.tree.usr_to_node[cursor.get_usr()] - else: - return + if cursor in self.tree.cursor_to_node: + node = self.tree.cursor_to_node[cursor] + elif cursor.get_usr() in self.tree.usr_to_node: + node = self.tree.usr_to_node[cursor.get_usr()] + else: + return - self.add_ref_node_id(node, elem) + self.add_ref_node_id(node, elem) - def type_to_md(self, tp, parent=None): - elem = ElementTree.Element('type') + def type_to_md(self, tp, parent=None): + elem = ElementTree.Element('type') - if tp.is_constant_array: - elem.set('size', str(tp.constant_array_size)) - elem.set('class', 'array') - elem.append(self.type_to_md(tp.element_type, parent)) - elif tp.is_function: - elem.set('class', 'function') + if tp.is_constant_array: + elem.set('size', str(tp.constant_array_size)) + elem.set('class', 'array') + elem.append(self.type_to_md(tp.element_type, parent)) + elif tp.is_function: + elem.set('class', 'function') - result = ElementTree.Element('result') - result.append(self.type_to_md(tp.function_result, parent)) - elem.append(result) + result = ElementTree.Element('result') + result.append(self.type_to_md(tp.function_result, parent)) + elem.append(result) - args = ElementTree.Element('arguments') - elem.append(args) + args = ElementTree.Element('arguments') + elem.append(args) - for arg in tp.function_arguments: - args.append(self.type_to_md(arg, parent)) - else: - elem.set('name', tp.typename_for(parent)) + for arg in tp.function_arguments: + args.append(self.type_to_md(arg, parent)) + else: + elem.set('name', tp.typename_for(parent)) - if len(tp.qualifier) > 0: - elem.set('qualifier', tp.qualifier_string) + if len(tp.qualifier) > 0: + elem.set('qualifier', tp.qualifier_string) - if tp.builtin: - elem.set('builtin', 'yes') + if tp.builtin: + elem.set('builtin', 'yes') - if tp.is_out: - elem.set('out', 'yes') + if tp.is_out: + elem.set('out', 'yes') - if tp.transfer_ownership != 'none': - elem.set('transfer-ownership', tp.transfer_ownership) + if tp.transfer_ownership != 'none': + elem.set('transfer-ownership', tp.transfer_ownership) - if tp.allow_none: - elem.set('allow-none', 'yes') + if tp.allow_none: + elem.set('allow-none', 'yes') - self.add_ref_id(tp.decl, elem) - return elem + self.add_ref_id(tp.decl, elem) + return elem - def enumvalue_to_md(self, node, elem): - elem.set('value', str(node.value)) + def enumvalue_to_md(self, node, elem): + elem.set('value', str(node.value)) - def enum_to_md(self, node, elem): - if not node.typedef is None: - elem.set('typedef', 'yes') + def enum_to_md(self, node, elem): + if not node.typedef is None: + elem.set('typedef', 'yes') - if node.isclass: - elem.set('class', 'yes') + if node.isclass: + elem.set('class', 'yes') - def struct_to_md(self, node, elem): - self.class_to_md(node, elem) + def struct_to_md(self, node, elem): + self.class_to_md(node, elem) - if not node.typedef is None: - elem.set('typedef', 'yes') + if not node.typedef is None: + elem.set('typedef', 'yes') - def templatetypeparameter_to_md(self, node, elem): - dt = node.default_type + def templatetypeparameter_to_md(self, node, elem): + dt = node.default_type - if not dt is None: - d = ElementTree.Element('default') + if not dt is None: + d = ElementTree.Element('default') - d.append(self.type_to_md(dt)) - elem.append(d) + d.append(self.type_to_md(dt)) + elem.append(d) - def templatenontypeparameter_to_md(self, node, elem): - elem.append(self.type_to_md(node.type)) + def templatenontypeparameter_to_md(self, node, elem): + elem.append(self.type_to_md(node.type)) - def function_to_md(self, node, elem): - if not (isinstance(node, nodes.Constructor) or - isinstance(node, nodes.Destructor)): - ret = ElementTree.Element('return') + def function_to_md(self, node, elem): + if not (isinstance(node, nodes.Constructor) or + isinstance(node, nodes.Destructor)): + ret = ElementTree.Element('return') - if not node.comment is None and hasattr(node.comment, 'returns') and node.comment.returns: - ret.append(self.doc_to_md(node, node.comment.returns)) + if not node.comment is None and hasattr(node.comment, 'returns') and node.comment.returns: + ret.append(self.doc_to_md(node, node.comment.returns)) - tp = self.type_to_md(node.return_type, node.parent) + tp = self.type_to_md(node.return_type, node.parent) - ret.append(tp) - elem.append(ret) + ret.append(tp) + elem.append(ret) - for arg in node.arguments: - ret = ElementTree.Element('argument') - ret.set('name', arg.name) - ret.set('id', arg.qid) + for arg in node.arguments: + ret = ElementTree.Element('argument') + ret.set('name', arg.name) + ret.set('id', arg.qid) - if not node.comment is None and arg.name in node.comment.params: - ret.append(self.doc_to_md(node, node.comment.params[arg.name])) + if not node.comment is None and arg.name in node.comment.params: + ret.append(self.doc_to_md(node, node.comment.params[arg.name])) - ret.append(self.type_to_md(arg.type, node.parent)) - elem.append(ret) + ret.append(self.type_to_md(arg.type, node.parent)) + elem.append(ret) - def method_to_md(self, node, elem): - self.function_to_md(node, elem) + def method_to_md(self, node, elem): + self.function_to_md(node, elem) - if len(node.override) > 0: - elem.set('override', 'yes') + if len(node.override) > 0: + elem.set('override', 'yes') - for ov in node.override: - ovelem = ElementTree.Element('override') + for ov in node.override: + ovelem = ElementTree.Element('override') - ovelem.set('name', ov.qid_to(node.qid)) - self.add_ref_node_id(ov, ovelem) + ovelem.set('name', ov.qid_to(node.qid)) + self.add_ref_node_id(ov, ovelem) - elem.append(ovelem) + elem.append(ovelem) - if node.virtual: - elem.set('virtual', 'yes') + if node.virtual: + elem.set('virtual', 'yes') - if node.static: - elem.set('static', 'yes') + if node.static: + elem.set('static', 'yes') - if node.abstract: - elem.set('abstract', 'yes') + if node.abstract: + elem.set('abstract', 'yes') - def typedef_to_md(self, node, elem): - elem.append(self.type_to_md(node.type, node)) + def typedef_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node)) - def typedef_to_md_ref(self, node, elem): - elem.append(self.type_to_md(node.type, node)) + def typedef_to_md_ref(self, node, elem): + elem.append(self.type_to_md(node.type, node)) - def variable_to_md(self, node, elem): - elem.append(self.type_to_md(node.type, node.parent)) + def variable_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node.parent)) - def property_to_md(self, node, elem): - elem.append(self.type_to_md(node.type, node.parent)) + def property_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node.parent)) - def set_access_attribute(self, node, elem): - if node.access == cindex.AccessSpecifier.PROTECTED: - elem.set('access', 'protected') - elif node.access == cindex.AccessSpecifier.PRIVATE: - elem.set('access', 'private') - elif node.access == cindex.AccessSpecifier.PUBLIC: - elem.set('access', 'public') + def set_access_attribute(self, node, elem): + if node.access == cindex.AccessSpecifier.PROTECTED: + elem.set('access', 'protected') + elif node.access == cindex.AccessSpecifier.PRIVATE: + elem.set('access', 'private') + elif node.access == cindex.AccessSpecifier.PUBLIC: + elem.set('access', 'public') - def process_bases(self, node, elem, bases, tagname): - for base in bases: - child = ElementTree.Element(tagname) + def process_bases(self, node, elem, bases, tagname): + for base in bases: + child = ElementTree.Element(tagname) - self.set_access_attribute(base, child) + self.set_access_attribute(base, child) - child.append(self.type_to_md(base.type, node)) + child.append(self.type_to_md(base.type, node)) - if base.node and not base.node.comment is None and base.node.comment.brief: - child.append(self.doc_to_md(base.node, base.node.comment.brief, 'brief')) + if base.node and not base.node.comment is None and base.node.comment.brief: + child.append(self.doc_to_md(base.node, base.node.comment.brief, 'brief')) - elem.append(child) + elem.append(child) - def process_subclasses(self, node, elem, subclasses, tagname): - for subcls in subclasses: - child = ElementTree.Element(tagname) + def process_subclasses(self, node, elem, subclasses, tagname): + for subcls in subclasses: + child = ElementTree.Element(tagname) - self.set_access_attribute(subcls, child) - self.add_ref_node_id(subcls, child) + self.set_access_attribute(subcls, child) + self.add_ref_node_id(subcls, child) - child.set('name', subcls.qid_to(node.qid)) + child.set('name', subcls.qid_to(node.qid)) - if not subcls.comment is None and subcls.comment.brief: - child.append(self.doc_to_md(subcls, subcls.comment.brief, 'brief')) + if not subcls.comment is None and subcls.comment.brief: + child.append(self.doc_to_md(subcls, subcls.comment.brief, 'brief')) - elem.append(child) + elem.append(child) - def class_to_md(self, node, elem): - self.process_bases(node, elem, node.bases, 'base') - self.process_bases(node, elem, node.implements, 'implements') + def class_to_md(self, node, elem): + self.process_bases(node, elem, node.bases, 'base') + self.process_bases(node, elem, node.implements, 'implements') - self.process_subclasses(node, elem, node.subclasses, 'subclass') - self.process_subclasses(node, elem, node.implemented_by, 'implementedby') + self.process_subclasses(node, elem, node.subclasses, 'subclass') + self.process_subclasses(node, elem, node.implemented_by, 'implementedby') - hasabstract = False - allabstract = True + hasabstract = False + allabstract = True - for method in node.methods: - if method.abstract: - hasabstract = True - else: - allabstract = False + for method in node.methods: + if method.abstract: + hasabstract = True + else: + allabstract = False - if hasabstract: - if allabstract: - elem.set('interface', 'true') - else: - elem.set('abstract', 'true') + if hasabstract: + if allabstract: + elem.set('interface', 'true') + else: + elem.set('abstract', 'true') - def field_to_md(self, node, elem): - elem.append(self.type_to_md(node.type, node.parent)) + def field_to_md(self, node, elem): + elem.append(self.type_to_md(node.type, node.parent)) - def doc_to_md(self, parent, doc, tagname='doc'): - doce = ElementTree.Element(tagname) + def doc_to_md(self, parent, doc, tagname='doc'): + doce = ElementTree.Element(tagname) - s = '' - last = None + s = '' + last = None - for component in doc.components: - if isinstance(component, utf8.string): - s += component - elif isinstance(component, example.Example): - # Make highlighting - if last is None: - doce.text = s - else: - last.tail = s + for component in doc.components: + if isinstance(component, utf8.string): + s += component + elif isinstance(component, example.Example): + # Make highlighting + if last is None: + doce.text = s + else: + last.tail = s - s = '' + s = '' - code = ElementTree.Element('code') - doce.append(code) + code = ElementTree.Element('code') + doce.append(code) - last = code + last = code - for item in component: - if item.classes is None: - s += item.text - else: - last.tail = s + for item in component: + if item.classes is None: + s += item.text + else: + last.tail = s - s = '' - par = code + s = '' + par = code - for cls in item.classes: - e = ElementTree.Element(cls) + for cls in item.classes: + e = ElementTree.Element(cls) - par.append(e) - par = e + par.append(e) + par = e - par.text = item.text - last = par + par.text = item.text + last = par - if last == code: - last.text = s - else: - last.tail = s + if last == code: + last.text = s + else: + last.tail = s - s = '' - last = code - else: - if last is None: - doce.text = s - else: - last.tail = s + s = '' + last = code + else: + if last is None: + doce.text = s + else: + last.tail = s - s = '' + s = '' - nds = component[0] - refname = component[1] + nds = component[0] + refname = component[1] - # Make multiple refs - for ci in range(len(nds)): - cc = nds[ci] + # Make multiple refs + for ci in range(len(nds)): + cc = nds[ci] - last = ElementTree.Element('ref') + last = ElementTree.Element('ref') - if refname: - last.text = refname - elif cc.title: - last.text=cc.title - else: - last.text = parent.qlbl_from(cc) + if refname: + last.text = refname + elif cc.title: + last.text=cc.title + else: + last.text = parent.qlbl_from(cc) - self.add_ref_node_id(cc, last) - - if ci != len(nds) - 1: - if ci == len(nds) - 2: - last.tail = ' and ' - else: - last.tail = ', ' - - doce.append(last) + self.add_ref_node_id(cc, last) + + if ci != len(nds) - 1: + if ci == len(nds) - 2: + last.tail = ' and ' + else: + last.tail = ', ' + + doce.append(last) + + if last is None: + doce.text = s + else: + last.tail = s + + return doce + + def call_type_specific(self, node, elem, fn): + clss = [node.__class__] - if last is None: - doce.text = s - else: - last.tail = s - - return doce + while len(clss) > 0: + cls = clss[0] + clss = clss[1:] - def call_type_specific(self, node, elem, fn): - clss = [node.__class__] + if cls == nodes.Node: + continue - while len(clss) > 0: - cls = clss[0] - clss = clss[1:] - - if cls == nodes.Node: - continue - - nm = cls.__name__.lower() + '_' + fn - - if hasattr(self, nm): - getattr(self, nm)(node, elem) - break - - if cls != nodes.Node: - clss.extend(cls.__bases__) - - def node_to_md(self, node): - elem = ElementTree.Element(node.classname) - props = node.props - - for prop in props: - if props[prop]: - elem.set(prop, props[prop]) - if node.cursor: - location=node.cursor.location.file.name - if location!='': - location=location.replace('\\','/') - location=location.replace('//','/') - if self.options.strip!=None: - location=location.replace(self.options.strip,'') - elem.set('location',location) - if not node.comment is None and node.comment.brief: - elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) - - if not node.comment is None and node.comment.doc: - elem.append(self.doc_to_md(node, node.comment.doc)) - - self.call_type_specific(node, elem, 'to_md') - - for child in node.sorted_children(): - if child.access == cindex.AccessSpecifier.PRIVATE: - continue - - self.refid(child) - - if self.is_page(child): - chelem = self.node_to_md_ref(child) - else: - chelem = self.node_to_md(child) - - elem.append(chelem) - - return elem - - def templated_to_md_ref(self, node, element): - for child in node.sorted_children(): - if not (isinstance(child, nodes.TemplateTypeParameter) or isinstance(child, nodes.TemplateNonTypeParameter)): - continue - - element.append(self.node_to_md(child)) - - def generate_page(self, node): - # ignore nodes containing no documentation - if not node.has_any_docs(): - return - elem = self.node_to_md(node) - self.namespace_separator='.' - if self.namespaces_as_directories==True: - self.namespace_separator='/' - self.write_md(elem, node.output_filename(self.namespace_separator) + '.md') - - def node_to_md_ref(self, node): - elem = ElementTree.Element(node.classname) - props = node.props - - # Add reference item to index - self.add_ref_node_id(node, elem) - - if 'title' in props: - elem.set('title', props['title']) - if 'name' in props: - elem.set('name', props['name']) - if 'weight' in props: - elem.set('weight', props['weight']) - - if not node.comment is None and node.comment.brief: - elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) - - self.call_type_specific(node, elem, 'to_md_ref') - - return elem - - def generate_node(self, node): - # Ignore private stuff - if node.access == cindex.AccessSpecifier.PRIVATE: - return - - self.refid(node) - - if self.is_page(node): - elem = self.node_to_md_ref(node) - if node.parent: - self.indexmap[node.parent].append(elem) - self.indexmap[node] = elem - - self.generate_page(node) - elif self.is_top(node): - self.index.append(self.node_to_md(node)) - - if isinstance(node, nodes.Namespace) or isinstance(node, nodes.Category): - # Go deep for namespaces and categories - Generator.generate_node(self, node) - elif isinstance(node, nodes.Class): - # Go deep, but only for inner classes - Generator.generate_node(self, node, lambda x: isinstance(x, nodes.Class)) + nm = cls.__name__.lower() + '_' + fn + + if hasattr(self, nm): + getattr(self, nm)(node, elem) + break + + if cls != nodes.Node: + clss.extend(cls.__bases__) + + def node_to_md(self, node): + elem = ElementTree.Element(node.classname) + props = node.props + + for prop in props: + if props[prop]: + elem.set(prop, props[prop]) + if node.cursor: + location=node.cursor.location.file.name + if location!='': + location=location.replace('\\','/') + location=location.replace('//','/') + if self.options.strip!=None: + location=location.replace(self.options.strip,'') + elem.set('location',location) + if not node.comment is None and node.comment.brief: + elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) + + if not node.comment is None and node.comment.doc: + elem.append(self.doc_to_md(node, node.comment.doc)) + + self.call_type_specific(node, elem, 'to_md') + + for child in node.sorted_children(): + if child.access == cindex.AccessSpecifier.PRIVATE: + continue + + self.refid(child) + + if self.is_page(child): + chelem = self.node_to_md_ref(child) + else: + chelem = self.node_to_md(child) + + elem.append(chelem) + + return elem + + def templated_to_md_ref(self, node, element): + for child in node.sorted_children(): + if not (isinstance(child, nodes.TemplateTypeParameter) or isinstance(child, nodes.TemplateNonTypeParameter)): + continue + + element.append(self.node_to_md(child)) + + def generate_page(self, node): + # ignore nodes containing no documentation + if not node.has_any_docs(): + return + elem = self.node_to_md(node) + self.namespace_separator='.' + if self.namespaces_as_directories==True: + self.namespace_separator='/' + self.write_md(elem, node.output_filename(self.namespace_separator) + '.md') + + def node_to_md_ref(self, node): + elem = ElementTree.Element(node.classname) + props = node.props + + # Add reference item to index + self.add_ref_node_id(node, elem) + + if 'title' in props: + elem.set('title', props['title']) + if 'name' in props: + elem.set('name', props['name']) + if 'weight' in props: + elem.set('weight', props['weight']) + + # can't put arbitrary text into brief, because markdown is replaced with html. + # if not node.comment is None and node.comment.brief: + # elem.append(self.doc_to_md(node, node.comment.brief, 'brief')) + + self.call_type_specific(node, elem, 'to_md_ref') + + return elem + + def generate_node(self, node): + # Ignore private stuff + if node.access == cindex.AccessSpecifier.PRIVATE: + return + + self.refid(node) + + if self.is_page(node): + elem = self.node_to_md_ref(node) + if node.parent: + self.indexmap[node.parent].append(elem) + self.indexmap[node] = elem + + self.generate_page(node) + elif self.is_top(node): + self.index.append(self.node_to_md(node)) + + if isinstance(node, nodes.Namespace) or isinstance(node, nodes.Category): + # Go deep for namespaces and categories + Generator.generate_node(self, node) + elif isinstance(node, nodes.Class): + # Go deep, but only for inner classes + Generator.generate_node(self, node, lambda x: isinstance(x, nodes.Class)) # vi:ts=4:et diff --git a/cldoc/generators/report.py b/cldoc/generators/report.py index 282fbf6..f7e5423 100644 --- a/cldoc/generators/report.py +++ b/cldoc/generators/report.py @@ -23,208 +23,208 @@ from xml.etree import ElementTree class Report: - Coverage = Struct.define('Coverage', name='', documented=[], undocumented=[]) + Coverage = Struct.define('Coverage', name='', documented=[], undocumented=[]) - def __init__(self, tree, options): - self.tree = tree - self.options = options + def __init__(self, tree, options): + self.tree = tree + self.options = options - def indent(self, elem, level=0): - i = "\n" + " " * level + def indent(self, elem, level=0): + i = "\n" + " " * level - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " - for e in elem: - self.indent(e, level + 1) + for e in elem: + self.indent(e, level + 1) - if not e.tail or not e.tail.strip(): - e.tail = i + " " - if not e.tail or not e.tail.strip(): - e.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i + if not e.tail or not e.tail.strip(): + e.tail = i + " " + if not e.tail or not e.tail.strip(): + e.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i - def make_location(self, loc): - elem = ElementTree.Element('location') + def make_location(self, loc): + elem = ElementTree.Element('location') - if self.options.basedir: - start = self.options.basedir - else: - start = os.curdir + if self.options.basedir: + start = self.options.basedir + else: + start = os.curdir - elem.set('file', os.path.relpath(str(loc.file), start)) - elem.set('line', str(loc.line)) - elem.set('column', str(loc.column)) + elem.set('file', os.path.relpath(str(loc.file), start)) + elem.set('line', str(loc.line)) + elem.set('column', str(loc.column)) - return elem + return elem - def arguments(self, root): - elem = ElementTree.Element('arguments') - root.append(elem) + def arguments(self, root): + elem = ElementTree.Element('arguments') + root.append(elem) - for node in self.tree.all_nodes: - if not isinstance(node, nodes.Function): - continue + for node in self.tree.all_nodes: + if not isinstance(node, nodes.Function): + continue - if node.access == cindex.AccessSpecifier.PRIVATE: - continue + if node.access == cindex.AccessSpecifier.PRIVATE: + continue - if node.comment is None: - continue + if node.comment is None: + continue - # Check documented arguments - notdocumented = [] - misspelled = [] + # Check documented arguments + notdocumented = [] + misspelled = [] - cm = node.comment - argnames = {} + cm = node.comment + argnames = {} - for name in node.argument_names: - argnames[name] = False + for name in node.argument_names: + argnames[name] = False - for k in cm.params: - if self._is_undocumented_comment(cm.params[k]): - continue + for k in cm.params: + if self._is_undocumented_comment(cm.params[k]): + continue - if k in argnames: - argnames[k] = True - else: - misspelled.append(k) + if k in argnames: + argnames[k] = True + else: + misspelled.append(k) - for k in argnames: - if not argnames[k]: - notdocumented.append(k) + for k in argnames: + if not argnames[k]: + notdocumented.append(k) - if node.return_type.typename != 'void' and not hasattr(cm, 'returns'): - missingret = True - elif hasattr(cm, 'returns') and self._is_undocumented_comment(cm.returns): - missingret = True - else: - missingret = False + if node.return_type.typename != 'void' and not hasattr(cm, 'returns'): + missingret = True + elif hasattr(cm, 'returns') and self._is_undocumented_comment(cm.returns): + missingret = True + else: + missingret = False - if len(notdocumented) > 0 or len(misspelled) > 0 or missingret: - e = ElementTree.Element('function') - e.set('id', node.qid) - e.set('name', node.name) + if len(notdocumented) > 0 or len(misspelled) > 0 or missingret: + e = ElementTree.Element('function') + e.set('id', node.qid) + e.set('name', node.name) - for loc in node.comment_locations: - e.append(self.make_location(loc)) + for loc in node.comment_locations: + e.append(self.make_location(loc)) - if missingret: - ee = ElementTree.Element('undocumented-return') - e.append(ee) + if missingret: + ee = ElementTree.Element('undocumented-return') + e.append(ee) - for ndoc in notdocumented: - ee = ElementTree.Element('undocumented') - ee.set('name', ndoc) - e.append(ee) + for ndoc in notdocumented: + ee = ElementTree.Element('undocumented') + ee.set('name', ndoc) + e.append(ee) - for mis in misspelled: - ee = ElementTree.Element('misspelled') - ee.set('name', mis) - e.append(ee) + for mis in misspelled: + ee = ElementTree.Element('misspelled') + ee.set('name', mis) + e.append(ee) - elem.append(e) + elem.append(e) - def _is_undocumented_comment(self, cm): - return not bool(cm) + def _is_undocumented_comment(self, cm): + return not bool(cm) - def coverage(self, root): - pertype = {} + def coverage(self, root): + pertype = {} - for node in self.tree.all_nodes: - cname = node.__class__.__name__ + for node in self.tree.all_nodes: + cname = node.__class__.__name__ - if node.access == cindex.AccessSpecifier.PRIVATE: - continue + if node.access == cindex.AccessSpecifier.PRIVATE: + continue - if not cname in pertype: - pertype[cname] = Report.Coverage(name=cname.lower()) + if not cname in pertype: + pertype[cname] = Report.Coverage(name=cname.lower()) - if not self._is_undocumented_comment(node.comment): - pertype[cname].documented.append(node) - else: - pertype[cname].undocumented.append(node) + if not self._is_undocumented_comment(node.comment): + pertype[cname].documented.append(node) + else: + pertype[cname].undocumented.append(node) - cov = ElementTree.Element('coverage') - root.append(cov) + cov = ElementTree.Element('coverage') + root.append(cov) - for item in pertype.values(): - elem = ElementTree.Element('type') - elem.set('name', item.name) - elem.set('documented', str(len(item.documented))) - elem.set('undocumented', str(len(item.undocumented))) + for item in pertype.values(): + elem = ElementTree.Element('type') + elem.set('name', item.name) + elem.set('documented', str(len(item.documented))) + elem.set('undocumented', str(len(item.undocumented))) - item.undocumented.sort(key=lambda x: x.qid) + item.undocumented.sort(key=lambda x: x.qid) - for undoc in item.undocumented: - e = ElementTree.Element('undocumented') - e.set('id', undoc.qid) - e.set('name', undoc.name) + for undoc in item.undocumented: + e = ElementTree.Element('undocumented') + e.set('id', undoc.qid) + e.set('name', undoc.name) - for loc in undoc.comment_locations: - e.append(self.make_location(loc)) + for loc in undoc.comment_locations: + e.append(self.make_location(loc)) - elem.append(e) + elem.append(e) - cov.append(elem) + cov.append(elem) - def references(self, root): - elem = ElementTree.Element('references') - root.append(elem) + def references(self, root): + elem = ElementTree.Element('references') + root.append(elem) - for node in self.tree.all_nodes: - if node.comment is None: - continue + for node in self.tree.all_nodes: + if node.comment is None: + continue - ee = None + ee = None - for name in node.comment.docstrings: - cm = getattr(node.comment, name) + for name in node.comment.docstrings: + cm = getattr(node.comment, name) - if not isinstance(cm, dict): - cm = {None: cm} + if not isinstance(cm, dict): + cm = {None: cm} - for k in cm: - en = None + for k in cm: + en = None - for component in cm[k].components: - if isinstance(component, Comment.UnresolvedReference): - if ee is None: - ee = ElementTree.Element(node.classname) + for component in cm[k].components: + if isinstance(component, Comment.UnresolvedReference): + if ee is None: + ee = ElementTree.Element(node.classname) - ee.set('name', node.name) - ee.set('id', node.qid) + ee.set('name', node.name) + ee.set('id', node.qid) - for loc in node.comment_locations: - ee.append(self.make_location(loc)) + for loc in node.comment_locations: + ee.append(self.make_location(loc)) - elem.append(ee) + elem.append(ee) - if en is None: - en = ElementTree.Element('doctype') + if en is None: + en = ElementTree.Element('doctype') - en.set('name', name) + en.set('name', name) - if not k is None: - en.set('component', k) + if not k is None: + en.set('component', k) - ee.append(en) + ee.append(en) - er = ElementTree.Element('ref') - er.set('name', component.orig) - en.append(er) + er = ElementTree.Element('ref') + er.set('name', component.orig) + en.append(er) - def generate(self, filename): - root = ElementTree.Element('report') - root.set('id', filename) - root.set('title', 'Documention generator') + def generate(self, filename): + root = ElementTree.Element('report') + root.set('id', filename) + root.set('title', 'Documention generator') - doc = ElementTree.Element('doc') - doc.text = """ + doc = ElementTree.Element('doc') + doc.text = """ This page provides a documentation coverage report. Any undocumented symbols are reported here together with the location of where you should document them. @@ -236,12 +236,12 @@ def generate(self, filename): 3. [References](#{0}/references): Unresolved cross references. """.format(filename) - root.append(doc) + root.append(doc) - self.coverage(root) - self.arguments(root) - self.references(root) + self.coverage(root) + self.arguments(root) + self.references(root) - return root + return root # vi:ts=4:et diff --git a/cldoc/generators/search.py b/cldoc/generators/search.py index 6732941..1300c40 100644 --- a/cldoc/generators/search.py +++ b/cldoc/generators/search.py @@ -17,33 +17,33 @@ from cldoc.struct import Struct class Search: - Record = Struct.define('Record', node=None, s='', id=0) + Record = Struct.define('Record', node=None, s='', id=0) - def __init__(self, tree): - self.records = [] - self.suffixes = [] - self.db = [] + def __init__(self, tree): + self.records = [] + self.suffixes = [] + self.db = [] - for node in tree.root.descendants(): - if not node._refid is None and node.access != cindex.AccessSpecifier.PRIVATE: - self.make_index(node) + for node in tree.root.descendants(): + if not node._refid is None and node.access != cindex.AccessSpecifier.PRIVATE: + self.make_index(node) - def make_index(self, node): - name = node.qid.lower() + def make_index(self, node): + name = node.qid.lower() - r = Search.Record(node=node, s=name, id=len(self.records)) - self.records.append(r) + r = Search.Record(node=node, s=name, id=len(self.records)) + self.records.append(r) - for i in range(len(name) - 3): - suffix = name[i:] + for i in range(len(name) - 3): + suffix = name[i:] - # Determine where to insert the suffix - idx = bisect.bisect_left(self.suffixes, suffix) + # Determine where to insert the suffix + idx = bisect.bisect_left(self.suffixes, suffix) - if idx != len(self.suffixes) and self.suffixes[idx] == suffix: - self.db[idx].append((r.id, i)) - else: - self.suffixes.insert(idx, suffix) - self.db.insert(idx, [(r.id, i)]) + if idx != len(self.suffixes) and self.suffixes[idx] == suffix: + self.db[idx].append((r.id, i)) + else: + self.suffixes.insert(idx, suffix) + self.db.insert(idx, [(r.id, i)]) # vi:ts=4:et diff --git a/cldoc/generators/xml.py b/cldoc/generators/xml.py index da7d8f0..bf93b45 100644 --- a/cldoc/generators/xml.py +++ b/cldoc/generators/xml.py @@ -24,549 +24,549 @@ from cldoc import fs class Xml(Generator): - def generate(self, outdir): - if not outdir: - outdir = 'xml' + def generate(self, outdir): + if not outdir: + outdir = 'xml' - try: - fs.fs.makedirs(outdir) - except OSError: - pass + try: + fs.fs.makedirs(outdir) + except OSError: + pass - ElementTree.register_namespace('gobject', 'http://jessevdk.github.com/cldoc/gobject/1.0') - ElementTree.register_namespace('cldoc', 'http://jessevdk.github.com/cldoc/1.0') + ElementTree.register_namespace('gobject', 'http://jessevdk.github.com/cldoc/gobject/1.0') + ElementTree.register_namespace('cldoc', 'http://jessevdk.github.com/cldoc/1.0') - self.index = ElementTree.Element('index') - self.written = {} + self.index = ElementTree.Element('index') + self.written = {} - self.indexmap = { - self.tree.root: self.index - } + self.indexmap = { + self.tree.root: self.index + } - cm = self.tree.root.comment + cm = self.tree.root.comment - if cm: - if cm.brief: - self.index.append(self.doc_to_xml(self.tree.root, cm.brief, 'brief')) + if cm: + if cm.brief: + self.index.append(self.doc_to_xml(self.tree.root, cm.brief, 'brief')) - if cm.doc: - self.index.append(self.doc_to_xml(self.tree.root, cm.doc)) + if cm.doc: + self.index.append(self.doc_to_xml(self.tree.root, cm.doc)) - Generator.generate(self, outdir) + Generator.generate(self, outdir) - if self.options.report: - self.add_report() + if self.options.report: + self.add_report() - self.write_xml(self.index, 'index.xml') + self.write_xml(self.index, 'index.xml') - print('Generated `{0}\''.format(outdir)) + print('Generated `{0}\''.format(outdir)) - def add_report(self): - from .report import Report + def add_report(self): + from .report import Report - reportname = 'report' + reportname = 'report' - while reportname + '.xml' in self.written: - reportname = '_' + reportname + while reportname + '.xml' in self.written: + reportname = '_' + reportname - page = Report(self.tree, self.options).generate(reportname) + page = Report(self.tree, self.options).generate(reportname) - elem = ElementTree.Element('report') - elem.set('name', 'Documentation generator') - elem.set('ref', reportname) + elem = ElementTree.Element('report') + elem.set('name', 'Documentation generator') + elem.set('ref', reportname) - self.index.append(elem) + self.index.append(elem) - self.write_xml(page, reportname + '.xml') + self.write_xml(page, reportname + '.xml') - def indent(self, elem, level=0): - i = "\n" + " " * level + def indent(self, elem, level=0): + i = "\n" + " " * level - if elem.tag == 'doc': - return + if elem.tag == 'doc': + return - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " - for e in elem: - self.indent(e, level + 1) + for e in elem: + self.indent(e, level + 1) - if not e.tail or not e.tail.strip(): - e.tail = i + " " - if not e.tail or not e.tail.strip(): - e.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i + if not e.tail or not e.tail.strip(): + e.tail = i + " " + if not e.tail or not e.tail.strip(): + e.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i - def write_xml(self, elem, fname): - self.written[fname] = True + def write_xml(self, elem, fname): + self.written[fname] = True - elem.attrib['xmlns'] = 'http://jessevdk.github.com/cldoc/1.0' + elem.attrib['xmlns'] = 'http://jessevdk.github.com/cldoc/1.0' - tree = ElementTree.ElementTree(elem) + tree = ElementTree.ElementTree(elem) - self.indent(tree.getroot()) + self.indent(tree.getroot()) - f = fs.fs.open(os.path.join(self.outdir, fname), 'w') - tree.write(f, encoding='utf-8', xml_declaration=True) - f.write('\n') + f = fs.fs.open(os.path.join(self.outdir, fname), 'w') + tree.write(f, encoding='utf-8', xml_declaration=True) + f.write('\n') - f.close() + f.close() - def is_page(self, node): - if node.force_page: - return True + def is_page(self, node): + if node.force_page: + return True - if isinstance(node, nodes.Struct) and node.is_anonymous: - return False + if isinstance(node, nodes.Struct) and node.is_anonymous: + return False - if isinstance(node, nodes.Class): - for child in node.children: - if not (isinstance(child, nodes.Field) or \ - isinstance(child, nodes.Variable) or \ - isinstance(child, nodes.TemplateTypeParameter)): - return True + if isinstance(node, nodes.Class): + for child in node.children: + if not (isinstance(child, nodes.Field) or \ + isinstance(child, nodes.Variable) or \ + isinstance(child, nodes.TemplateTypeParameter)): + return True - return False + return False - pagecls = [nodes.Namespace, nodes.Category, nodes.Root] + pagecls = [nodes.Namespace, nodes.Category, nodes.Root] - for cls in pagecls: - if isinstance(node, cls): - return True + for cls in pagecls: + if isinstance(node, cls): + return True - if isinstance(node, nodes.Typedef) and len(node.children) > 0: - return True + if isinstance(node, nodes.Typedef) and len(node.children) > 0: + return True - return False + return False - def is_top(self, node): - if self.is_page(node): - return True + def is_top(self, node): + if self.is_page(node): + return True - if node.parent == self.tree.root: - return True + if node.parent == self.tree.root: + return True - return False + return False - def refid(self, node): - if not node._refid is None: - return node._refid + def refid(self, node): + if not node._refid is None: + return node._refid - parent = node + parent = node - meid = node.qid + meid = node.qid - if not node.parent or (isinstance(node.parent, nodes.Root) and not self.is_page(node)): - return 'index#' + meid + if not node.parent or (isinstance(node.parent, nodes.Root) and not self.is_page(node)): + return 'index#' + meid - # Find topmost parent - while not self.is_page(parent): - parent = parent.parent + # Find topmost parent + while not self.is_page(parent): + parent = parent.parent - if not node is None: - node._refid = parent.qid + '#' + meid - return node._refid - else: - return None + if not node is None: + node._refid = parent.qid + '#' + meid + return node._refid + else: + return None - def add_ref_node_id(self, node, elem): - r = self.refid(node) + def add_ref_node_id(self, node, elem): + r = self.refid(node) - if not r is None: - elem.set('ref', r) + if not r is None: + elem.set('ref', r) - def add_ref_id(self, cursor, elem): - if not cursor: - return + def add_ref_id(self, cursor, elem): + if not cursor: + return - if cursor in self.tree.cursor_to_node: - node = self.tree.cursor_to_node[cursor] - elif cursor.get_usr() in self.tree.usr_to_node: - node = self.tree.usr_to_node[cursor.get_usr()] - else: - return + if cursor in self.tree.cursor_to_node: + node = self.tree.cursor_to_node[cursor] + elif cursor.get_usr() in self.tree.usr_to_node: + node = self.tree.usr_to_node[cursor.get_usr()] + else: + return - self.add_ref_node_id(node, elem) + self.add_ref_node_id(node, elem) - def type_to_xml(self, tp, parent=None): - elem = ElementTree.Element('type') + def type_to_xml(self, tp, parent=None): + elem = ElementTree.Element('type') - if tp.is_constant_array: - elem.set('size', str(tp.constant_array_size)) - elem.set('class', 'array') - elem.append(self.type_to_xml(tp.element_type, parent)) - elif tp.is_function: - elem.set('class', 'function') + if tp.is_constant_array: + elem.set('size', str(tp.constant_array_size)) + elem.set('class', 'array') + elem.append(self.type_to_xml(tp.element_type, parent)) + elif tp.is_function: + elem.set('class', 'function') - result = ElementTree.Element('result') - result.append(self.type_to_xml(tp.function_result, parent)) - elem.append(result) + result = ElementTree.Element('result') + result.append(self.type_to_xml(tp.function_result, parent)) + elem.append(result) - args = ElementTree.Element('arguments') - elem.append(args) + args = ElementTree.Element('arguments') + elem.append(args) - for arg in tp.function_arguments: - args.append(self.type_to_xml(arg, parent)) - else: - elem.set('name', tp.typename_for(parent)) + for arg in tp.function_arguments: + args.append(self.type_to_xml(arg, parent)) + else: + elem.set('name', tp.typename_for(parent)) - if len(tp.qualifier) > 0: - elem.set('qualifier', tp.qualifier_string) + if len(tp.qualifier) > 0: + elem.set('qualifier', tp.qualifier_string) - if tp.builtin: - elem.set('builtin', 'yes') + if tp.builtin: + elem.set('builtin', 'yes') - if tp.is_out: - elem.set('out', 'yes') + if tp.is_out: + elem.set('out', 'yes') - if tp.transfer_ownership != 'none': - elem.set('transfer-ownership', tp.transfer_ownership) + if tp.transfer_ownership != 'none': + elem.set('transfer-ownership', tp.transfer_ownership) - if tp.allow_none: - elem.set('allow-none', 'yes') + if tp.allow_none: + elem.set('allow-none', 'yes') - self.add_ref_id(tp.decl, elem) - return elem + self.add_ref_id(tp.decl, elem) + return elem - def enumvalue_to_xml(self, node, elem): - elem.set('value', str(node.value)) + def enumvalue_to_xml(self, node, elem): + elem.set('value', str(node.value)) - def enum_to_xml(self, node, elem): - if not node.typedef is None: - elem.set('typedef', 'yes') + def enum_to_xml(self, node, elem): + if not node.typedef is None: + elem.set('typedef', 'yes') - if node.isclass: - elem.set('class', 'yes') + if node.isclass: + elem.set('class', 'yes') - def struct_to_xml(self, node, elem): - self.class_to_xml(node, elem) + def struct_to_xml(self, node, elem): + self.class_to_xml(node, elem) - if not node.typedef is None: - elem.set('typedef', 'yes') + if not node.typedef is None: + elem.set('typedef', 'yes') - def templatetypeparameter_to_xml(self, node, elem): - dt = node.default_type + def templatetypeparameter_to_xml(self, node, elem): + dt = node.default_type - if not dt is None: - d = ElementTree.Element('default') + if not dt is None: + d = ElementTree.Element('default') - d.append(self.type_to_xml(dt)) - elem.append(d) + d.append(self.type_to_xml(dt)) + elem.append(d) - def templatenontypeparameter_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type)) + def templatenontypeparameter_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type)) - def function_to_xml(self, node, elem): - if not (isinstance(node, nodes.Constructor) or - isinstance(node, nodes.Destructor)): - ret = ElementTree.Element('return') + def function_to_xml(self, node, elem): + if not (isinstance(node, nodes.Constructor) or + isinstance(node, nodes.Destructor)): + ret = ElementTree.Element('return') - if not node.comment is None and hasattr(node.comment, 'returns') and node.comment.returns: - ret.append(self.doc_to_xml(node, node.comment.returns)) + if not node.comment is None and hasattr(node.comment, 'returns') and node.comment.returns: + ret.append(self.doc_to_xml(node, node.comment.returns)) - tp = self.type_to_xml(node.return_type, node.parent) + tp = self.type_to_xml(node.return_type, node.parent) - ret.append(tp) - elem.append(ret) + ret.append(tp) + elem.append(ret) - for arg in node.arguments: - ret = ElementTree.Element('argument') - ret.set('name', arg.name) - ret.set('id', arg.qid) + for arg in node.arguments: + ret = ElementTree.Element('argument') + ret.set('name', arg.name) + ret.set('id', arg.qid) - if not node.comment is None and arg.name in node.comment.params: - ret.append(self.doc_to_xml(node, node.comment.params[arg.name])) + if not node.comment is None and arg.name in node.comment.params: + ret.append(self.doc_to_xml(node, node.comment.params[arg.name])) - ret.append(self.type_to_xml(arg.type, node.parent)) - elem.append(ret) + ret.append(self.type_to_xml(arg.type, node.parent)) + elem.append(ret) - def method_to_xml(self, node, elem): - self.function_to_xml(node, elem) + def method_to_xml(self, node, elem): + self.function_to_xml(node, elem) - if len(node.override) > 0: - elem.set('override', 'yes') + if len(node.override) > 0: + elem.set('override', 'yes') - for ov in node.override: - ovelem = ElementTree.Element('override') + for ov in node.override: + ovelem = ElementTree.Element('override') - ovelem.set('name', ov.qid_to(node.qid)) - self.add_ref_node_id(ov, ovelem) + ovelem.set('name', ov.qid_to(node.qid)) + self.add_ref_node_id(ov, ovelem) - elem.append(ovelem) + elem.append(ovelem) - if node.virtual: - elem.set('virtual', 'yes') + if node.virtual: + elem.set('virtual', 'yes') - if node.static: - elem.set('static', 'yes') + if node.static: + elem.set('static', 'yes') - if node.abstract: - elem.set('abstract', 'yes') + if node.abstract: + elem.set('abstract', 'yes') - def typedef_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node)) + def typedef_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node)) - def typedef_to_xml_ref(self, node, elem): - elem.append(self.type_to_xml(node.type, node)) + def typedef_to_xml_ref(self, node, elem): + elem.append(self.type_to_xml(node.type, node)) - def variable_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node.parent)) + def variable_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node.parent)) - def property_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node.parent)) + def property_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node.parent)) - def set_access_attribute(self, node, elem): - if node.access == cindex.AccessSpecifier.PROTECTED: - elem.set('access', 'protected') - elif node.access == cindex.AccessSpecifier.PRIVATE: - elem.set('access', 'private') - elif node.access == cindex.AccessSpecifier.PUBLIC: - elem.set('access', 'public') + def set_access_attribute(self, node, elem): + if node.access == cindex.AccessSpecifier.PROTECTED: + elem.set('access', 'protected') + elif node.access == cindex.AccessSpecifier.PRIVATE: + elem.set('access', 'private') + elif node.access == cindex.AccessSpecifier.PUBLIC: + elem.set('access', 'public') - def process_bases(self, node, elem, bases, tagname): - for base in bases: - child = ElementTree.Element(tagname) + def process_bases(self, node, elem, bases, tagname): + for base in bases: + child = ElementTree.Element(tagname) - self.set_access_attribute(base, child) + self.set_access_attribute(base, child) - child.append(self.type_to_xml(base.type, node)) + child.append(self.type_to_xml(base.type, node)) - if base.node and not base.node.comment is None and base.node.comment.brief: - child.append(self.doc_to_xml(base.node, base.node.comment.brief, 'brief')) + if base.node and not base.node.comment is None and base.node.comment.brief: + child.append(self.doc_to_xml(base.node, base.node.comment.brief, 'brief')) - elem.append(child) + elem.append(child) - def process_subclasses(self, node, elem, subclasses, tagname): - for subcls in subclasses: - child = ElementTree.Element(tagname) + def process_subclasses(self, node, elem, subclasses, tagname): + for subcls in subclasses: + child = ElementTree.Element(tagname) - self.set_access_attribute(subcls, child) - self.add_ref_node_id(subcls, child) + self.set_access_attribute(subcls, child) + self.add_ref_node_id(subcls, child) - child.set('name', subcls.qid_to(node.qid)) + child.set('name', subcls.qid_to(node.qid)) - if not subcls.comment is None and subcls.comment.brief: - child.append(self.doc_to_xml(subcls, subcls.comment.brief, 'brief')) + if not subcls.comment is None and subcls.comment.brief: + child.append(self.doc_to_xml(subcls, subcls.comment.brief, 'brief')) - elem.append(child) + elem.append(child) - def class_to_xml(self, node, elem): - self.process_bases(node, elem, node.bases, 'base') - self.process_bases(node, elem, node.implements, 'implements') + def class_to_xml(self, node, elem): + self.process_bases(node, elem, node.bases, 'base') + self.process_bases(node, elem, node.implements, 'implements') - self.process_subclasses(node, elem, node.subclasses, 'subclass') - self.process_subclasses(node, elem, node.implemented_by, 'implementedby') + self.process_subclasses(node, elem, node.subclasses, 'subclass') + self.process_subclasses(node, elem, node.implemented_by, 'implementedby') - hasabstract = False - allabstract = True + hasabstract = False + allabstract = True - for method in node.methods: - if method.abstract: - hasabstract = True - else: - allabstract = False + for method in node.methods: + if method.abstract: + hasabstract = True + else: + allabstract = False - if hasabstract: - if allabstract: - elem.set('interface', 'true') - else: - elem.set('abstract', 'true') + if hasabstract: + if allabstract: + elem.set('interface', 'true') + else: + elem.set('abstract', 'true') - def field_to_xml(self, node, elem): - elem.append(self.type_to_xml(node.type, node.parent)) + def field_to_xml(self, node, elem): + elem.append(self.type_to_xml(node.type, node.parent)) - def doc_to_xml(self, parent, doc, tagname='doc'): - doce = ElementTree.Element(tagname) + def doc_to_xml(self, parent, doc, tagname='doc'): + doce = ElementTree.Element(tagname) - s = '' - last = None + s = '' + last = None - for component in doc.components: - if isinstance(component, utf8.string): - s += component - elif isinstance(component, example.Example): - # Make highlighting - if last is None: - doce.text = s - else: - last.tail = s + for component in doc.components: + if isinstance(component, utf8.string): + s += component + elif isinstance(component, example.Example): + # Make highlighting + if last is None: + doce.text = s + else: + last.tail = s - s = '' + s = '' - code = ElementTree.Element('code') - doce.append(code) + code = ElementTree.Element('code') + doce.append(code) - last = code + last = code - for item in component: - if item.classes is None: - s += item.text - else: - last.tail = s + for item in component: + if item.classes is None: + s += item.text + else: + last.tail = s - s = '' - par = code + s = '' + par = code - for cls in item.classes: - e = ElementTree.Element(cls) + for cls in item.classes: + e = ElementTree.Element(cls) - par.append(e) - par = e + par.append(e) + par = e - par.text = item.text - last = par + par.text = item.text + last = par - if last == code: - last.text = s - else: - last.tail = s + if last == code: + last.text = s + else: + last.tail = s - s = '' - last = code - else: - if last is None: - doce.text = s - else: - last.tail = s + s = '' + last = code + else: + if last is None: + doce.text = s + else: + last.tail = s - s = '' + s = '' - nds = component[0] - refname = component[1] + nds = component[0] + refname = component[1] - # Make multiple refs - for ci in range(len(nds)): - cc = nds[ci] + # Make multiple refs + for ci in range(len(nds)): + cc = nds[ci] - last = ElementTree.Element('ref') + last = ElementTree.Element('ref') - if refname: - last.text = refname - else: - last.text = parent.qlbl_from(cc) + if refname: + last.text = refname + else: + last.text = parent.qlbl_from(cc) - self.add_ref_node_id(cc, last) + self.add_ref_node_id(cc, last) - if ci != len(nds) - 1: - if ci == len(nds) - 2: - last.tail = ' and ' - else: - last.tail = ', ' + if ci != len(nds) - 1: + if ci == len(nds) - 2: + last.tail = ' and ' + else: + last.tail = ', ' - doce.append(last) + doce.append(last) - if last is None: - doce.text = s - else: - last.tail = s + if last is None: + doce.text = s + else: + last.tail = s - return doce + return doce - def call_type_specific(self, node, elem, fn): - clss = [node.__class__] + def call_type_specific(self, node, elem, fn): + clss = [node.__class__] - while len(clss) > 0: - cls = clss[0] - clss = clss[1:] + while len(clss) > 0: + cls = clss[0] + clss = clss[1:] - if cls == nodes.Node: - continue + if cls == nodes.Node: + continue - nm = cls.__name__.lower() + '_' + fn + nm = cls.__name__.lower() + '_' + fn - if hasattr(self, nm): - getattr(self, nm)(node, elem) - break + if hasattr(self, nm): + getattr(self, nm)(node, elem) + break - if cls != nodes.Node: - clss.extend(cls.__bases__) + if cls != nodes.Node: + clss.extend(cls.__bases__) - def node_to_xml(self, node): - elem = ElementTree.Element(node.classname) - props = node.props + def node_to_xml(self, node): + elem = ElementTree.Element(node.classname) + props = node.props - for prop in props: - if props[prop]: - elem.set(prop, props[prop]) + for prop in props: + if props[prop]: + elem.set(prop, props[prop]) - if not node.comment is None and node.comment.brief: - elem.append(self.doc_to_xml(node, node.comment.brief, 'brief')) + if not node.comment is None and node.comment.brief: + elem.append(self.doc_to_xml(node, node.comment.brief, 'brief')) - if not node.comment is None and node.comment.doc: - elem.append(self.doc_to_xml(node, node.comment.doc)) + if not node.comment is None and node.comment.doc: + elem.append(self.doc_to_xml(node, node.comment.doc)) - self.call_type_specific(node, elem, 'to_xml') + self.call_type_specific(node, elem, 'to_xml') - for child in node.sorted_children(): - if child.access == cindex.AccessSpecifier.PRIVATE: - continue + for child in node.sorted_children(): + if child.access == cindex.AccessSpecifier.PRIVATE: + continue - self.refid(child) + self.refid(child) - if self.is_page(child): - chelem = self.node_to_xml_ref(child) - else: - chelem = self.node_to_xml(child) + if self.is_page(child): + chelem = self.node_to_xml_ref(child) + else: + chelem = self.node_to_xml(child) - elem.append(chelem) + elem.append(chelem) - return elem + return elem - def templated_to_xml_ref(self, node, element): - for child in node.sorted_children(): - if not (isinstance(child, nodes.TemplateTypeParameter) or isinstance(child, nodes.TemplateNonTypeParameter)): - continue + def templated_to_xml_ref(self, node, element): + for child in node.sorted_children(): + if not (isinstance(child, nodes.TemplateTypeParameter) or isinstance(child, nodes.TemplateNonTypeParameter)): + continue - element.append(self.node_to_xml(child)) + element.append(self.node_to_xml(child)) - def generate_page(self, node): - elem = self.node_to_xml(node) - self.write_xml(elem, node.qid.replace('::', '.').replace(':', '.') + '.xml') + def generate_page(self, node): + elem = self.node_to_xml(node) + self.write_xml(elem, node.qid.replace('::', '.').replace(':', '.') + '.xml') - def node_to_xml_ref(self, node): - elem = ElementTree.Element(node.classname) - props = node.props + def node_to_xml_ref(self, node): + elem = ElementTree.Element(node.classname) + props = node.props - # Add reference item to index - self.add_ref_node_id(node, elem) + # Add reference item to index + self.add_ref_node_id(node, elem) - if 'title' in props: - elem.set('title', props['title']) - if 'name' in props: - elem.set('name', props['name']) + if 'title' in props: + elem.set('title', props['title']) + if 'name' in props: + elem.set('name', props['name']) - if not node.comment is None and node.comment.brief: - elem.append(self.doc_to_xml(node, node.comment.brief, 'brief')) + if not node.comment is None and node.comment.brief: + elem.append(self.doc_to_xml(node, node.comment.brief, 'brief')) - self.call_type_specific(node, elem, 'to_xml_ref') + self.call_type_specific(node, elem, 'to_xml_ref') - return elem + return elem - def generate_node(self, node): - # Ignore private stuff - if node.access == cindex.AccessSpecifier.PRIVATE: - return + def generate_node(self, node): + # Ignore private stuff + if node.access == cindex.AccessSpecifier.PRIVATE: + return - self.refid(node) + self.refid(node) - if self.is_page(node): - elem = self.node_to_xml_ref(node) + if self.is_page(node): + elem = self.node_to_xml_ref(node) - self.indexmap[node.parent].append(elem) - self.indexmap[node] = elem + self.indexmap[node.parent].append(elem) + self.indexmap[node] = elem - self.generate_page(node) - elif self.is_top(node): - self.index.append(self.node_to_xml(node)) + self.generate_page(node) + elif self.is_top(node): + self.index.append(self.node_to_xml(node)) - if isinstance(node, nodes.Namespace) or isinstance(node, nodes.Category): - # Go deep for namespaces and categories - Generator.generate_node(self, node) - elif isinstance(node, nodes.Class): - # Go deep, but only for inner classes - Generator.generate_node(self, node, lambda x: isinstance(x, nodes.Class)) + if isinstance(node, nodes.Namespace) or isinstance(node, nodes.Category): + # Go deep for namespaces and categories + Generator.generate_node(self, node) + elif isinstance(node, nodes.Class): + # Go deep, but only for inner classes + Generator.generate_node(self, node, lambda x: isinstance(x, nodes.Class)) # vi:ts=4:et diff --git a/cldoc/includepaths.py b/cldoc/includepaths.py index ba6a374..8a8d1e3 100644 --- a/cldoc/includepaths.py +++ b/cldoc/includepaths.py @@ -13,45 +13,45 @@ import os, subprocess, sys def flags(f): - devnull = open(os.devnull) + devnull = open(os.devnull) - try: - p = subprocess.Popen(['clang++', '-E', '-xc++'] + f + ['-v', '-'], - stdin=devnull, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - except OSError as e: - sys.stderr.write("\nFatal: Failed to run clang++ to obtain system include headers, please install clang++ to use cldoc\n") + try: + p = subprocess.Popen(['clang++', '-E', '-xc++'] + f + ['-v', '-'], + stdin=devnull, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + except OSError as e: + sys.stderr.write("\nFatal: Failed to run clang++ to obtain system include headers, please install clang++ to use cldoc\n") - message = str(e) + message = str(e) - if message: - sys.stderr.write(" Error message: " + message + "\n") + if message: + sys.stderr.write(" Error message: " + message + "\n") - sys.stderr.write("\n") - sys.exit(1) + sys.stderr.write("\n") + sys.exit(1) - devnull.close() + devnull.close() - lines = p.communicate()[1].splitlines() - init = False - paths = [] + lines = p.communicate()[1].splitlines() + init = False + paths = [] - for line in lines: - if line.startswith('#include <...>'): - init = True - elif line.startswith('End of search list.'): - init = False - elif init: - p = line.strip() + for line in lines: + if line.startswith('#include <...>'): + init = True + elif line.startswith('End of search list.'): + init = False + elif init: + p = line.strip() - suffix = ' (framework directory)' + suffix = ' (framework directory)' - if p.endswith(suffix): - p = p[:-len(suffix)] + if p.endswith(suffix): + p = p[:-len(suffix)] - paths.append(p) + paths.append(p) - return ['-I{0}'.format(x) for x in paths] + f + return ['-I{0}'.format(x) for x in paths] + f # vi:ts=4:et diff --git a/cldoc/inspecttree.py b/cldoc/inspecttree.py index 1f8066d..64c12ee 100644 --- a/cldoc/inspecttree.py +++ b/cldoc/inspecttree.py @@ -16,111 +16,111 @@ import os, sys def inspect_print_row(a, b, link=None): - from xml.sax.saxutils import escape + from xml.sax.saxutils import escape - b = escape(str(b)) + b = escape(str(b)) - if link: - b = "" + b + "" + if link: + b = "" + b + "" - print "%s%s" % (escape(str(a)), b) + print "%s%s" % (escape(str(a)), b) def inspect_print_subtype(name, tp, subtype, indent=1): - if not subtype or tp == subtype or subtype.kind == cindex.TypeKind.INVALID: - return + if not subtype or tp == subtype or subtype.kind == cindex.TypeKind.INVALID: + return - inspect_print_row('  ' * indent + '→ .' + name + '.kind', subtype.kind) - inspect_print_row('  ' * indent + '→ .' + name + '.spelling', subtype.kind.spelling) - inspect_print_row('  ' * indent + '→ .' + name + '.is_const_qualified', subtype.is_const_qualified()) + inspect_print_row('  ' * indent + '→ .' + name + '.kind', subtype.kind) + inspect_print_row('  ' * indent + '→ .' + name + '.spelling', subtype.kind.spelling) + inspect_print_row('  ' * indent + '→ .' + name + '.is_const_qualified', subtype.is_const_qualified()) - if subtype.kind == cindex.TypeKind.CONSTANTARRAY: - etype = subtype.get_array_element_type() - num = subtype.get_array_size() + if subtype.kind == cindex.TypeKind.CONSTANTARRAY: + etype = subtype.get_array_element_type() + num = subtype.get_array_size() - inspect_print_subtype('array_type', subtype, etype, indent + 1) - inspect_print_row(' ' * (indent + 1) + '→ .size', str(num)) + inspect_print_subtype('array_type', subtype, etype, indent + 1) + inspect_print_row(' ' * (indent + 1) + '→ .size', str(num)) - decl = subtype.get_declaration() + decl = subtype.get_declaration() - if decl: - inspect_print_row('  ' * indent + '→ .' + name + '.declaration', decl.displayname, decl.get_usr()) + if decl: + inspect_print_row('  ' * indent + '→ .' + name + '.declaration', decl.displayname, decl.get_usr()) - inspect_print_subtype('get_canonical', subtype, subtype.get_canonical(), indent + 1) - inspect_print_subtype('get_pointee', subtype, subtype.get_pointee(), indent + 1) - inspect_print_subtype('get_result', subtype, subtype.get_result(), indent + 1) + inspect_print_subtype('get_canonical', subtype, subtype.get_canonical(), indent + 1) + inspect_print_subtype('get_pointee', subtype, subtype.get_pointee(), indent + 1) + inspect_print_subtype('get_result', subtype, subtype.get_result(), indent + 1) def inspect_cursor(tree, cursor, indent): - from xml.sax.saxutils import escape + from xml.sax.saxutils import escape - if not cursor.location.file: - return + if not cursor.location.file: + return - if not str(cursor.location.file) in tree.files: - return + if not str(cursor.location.file) in tree.files: + return - print "" + print "
" - inspect_print_row('kind', cursor.kind) - inspect_print_row('  → .is_declaration', cursor.kind.is_declaration()) - inspect_print_row('  → .is_reference', cursor.kind.is_reference()) - inspect_print_row('  → .is_expression', cursor.kind.is_expression()) - inspect_print_row('  → .is_statement', cursor.kind.is_statement()) - inspect_print_row('  → .is_attribute', cursor.kind.is_attribute()) - inspect_print_row('  → .is_invalid', cursor.kind.is_invalid()) - inspect_print_row('  → .is_preprocessing', cursor.kind.is_preprocessing()) + inspect_print_row('kind', cursor.kind) + inspect_print_row('  → .is_declaration', cursor.kind.is_declaration()) + inspect_print_row('  → .is_reference', cursor.kind.is_reference()) + inspect_print_row('  → .is_expression', cursor.kind.is_expression()) + inspect_print_row('  → .is_statement', cursor.kind.is_statement()) + inspect_print_row('  → .is_attribute', cursor.kind.is_attribute()) + inspect_print_row('  → .is_invalid', cursor.kind.is_invalid()) + inspect_print_row('  → .is_preprocessing', cursor.kind.is_preprocessing()) - inspect_print_subtype('type', None, cursor.type, 0) + inspect_print_subtype('type', None, cursor.type, 0) - inspect_print_row('usr', cursor.get_usr()) - inspect_print_row('spelling', cursor.spelling) - inspect_print_row('displayname', cursor.displayname) - inspect_print_row('location', "%s (%d:%d - %d:%d)" % (os.path.basename(str(cursor.location.file)), cursor.extent.start.line, cursor.extent.start.column, cursor.extent.end.line, cursor.extent.end.column)) - inspect_print_row('is_definition', cursor.is_definition()) - inspect_print_row('is_virtual_method', cursor.is_virtual_method()) - inspect_print_row('is_static_method', cursor.is_static_method()) + inspect_print_row('usr', cursor.get_usr()) + inspect_print_row('spelling', cursor.spelling) + inspect_print_row('displayname', cursor.displayname) + inspect_print_row('location', "%s (%d:%d - %d:%d)" % (os.path.basename(str(cursor.location.file)), cursor.extent.start.line, cursor.extent.start.column, cursor.extent.end.line, cursor.extent.end.column)) + inspect_print_row('is_definition', cursor.is_definition()) + inspect_print_row('is_virtual_method', cursor.is_virtual_method()) + inspect_print_row('is_static_method', cursor.is_static_method()) - spec = cursor.access_specifier + spec = cursor.access_specifier - if not spec is None: - inspect_print_row('access_specifier', spec) + if not spec is None: + inspect_print_row('access_specifier', spec) - defi = cursor.get_definition() + defi = cursor.get_definition() - if defi and defi != cursor: - inspect_print_row('definition', defi.displayname, link=defi.get_usr()) + if defi and defi != cursor: + inspect_print_row('definition', defi.displayname, link=defi.get_usr()) - if cursor.kind == cindex.CursorKind.CXX_METHOD: - for t in cursor.type.argument_types(): - inspect_print_subtype('argument', None, t) + if cursor.kind == cindex.CursorKind.CXX_METHOD: + for t in cursor.type.argument_types(): + inspect_print_subtype('argument', None, t) - print "
" + print "" def inspect_cursors(tree, cursors, indent=0): - for cursor in cursors: - inspect_cursor(tree, cursor, indent) + for cursor in cursors: + inspect_cursor(tree, cursor, indent) - if (not cursor.location.file) or str(cursor.location.file) in tree.files: - inspect_cursors(tree, cursor.get_children(), indent + 1) + if (not cursor.location.file) or str(cursor.location.file) in tree.files: + inspect_cursors(tree, cursor.get_children(), indent + 1) def inspect_tokens(tree, filename, tu): - it = tu.get_tokens(extent=tu.get_extent(filename, (0, os.stat(filename).st_size))) + it = tu.get_tokens(extent=tu.get_extent(filename, (0, os.stat(filename).st_size))) - print "" + print "
" - for token in it: - print "" - print "" % (token.kind,) - print "" - print "" % (token.cursor.kind,) - print "" % (token.extent.start.line, token.extent.start.column,) - print "" + for token in it: + print "" + print "" % (token.kind,) + print "" + print "" % (token.cursor.kind,) + print "" % (token.extent.start.line, token.extent.start.column,) + print "" - print "
%s" + token.spelling + "%s%d:%d
%s" + token.spelling + "%s%d:%d
" + print "" def inspect(tree): - index = cindex.Index.create() + index = cindex.Index.create() - print """ + print """ - -""") - - for f in tree.files: - tu = index.parse(f, tree.flags) - - if not tu: - sys.stderr.write("Could not parse file %s...\n" % (f,)) - sys.exit(1) - - print("
" + f + "
") - - inspect_tokens(tree, f, tu) - - # Recursively inspect cursors - inspect_cursors(tree, tu.cursor.get_children()) - - print("
") - - print("\n") - -# vi:ts=4:et From 89aaf687540e484ee2f12deca9e1d0652ea0ef25 Mon Sep 17 00:00:00 2001 From: Roderick Kennedy Date: Sun, 14 Jul 2019 23:23:03 +0100 Subject: [PATCH 27/32] Fix post-generate call output to pipe to main stdout. Add .html to generated links. Do not recurse included headers in clang calls. Clean ref directory in test.py --- cldoc/cmdgenerate.py | 16 +++++++++++++--- cldoc/generators/__init__.py | 2 -- cldoc/generators/md.py | 7 +++++-- cldoc/tree.py | 5 +++-- scripts/test.py | 7 ++++--- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index 321a196..1a28e6c 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -33,8 +33,15 @@ def run_generate(t, opts): generator_md.generate(baseout) if opts.post!=None and opts.post != '': args=opts.post.split(' ') - ret=subprocess.call(args,shell=True) - if ret!=0: + process = subprocess.Popen(args, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) + for line in process.stdout: + sys.stdout.write(str(line.decode('UTF-8'))) + if process.stderr: + for line in process.stderr: + sys.stdout.write(str(line.decode('UTF-8'))) + #ret=subprocess.call(args,shell=True) + process.wait() + if process.returncode!=0: sys.stderr.write('Error: failed to run post process '+opts.post+'\n') sys.exit(1) @@ -147,7 +154,10 @@ def run(args): r = glob.glob(opts.clean+'/*') for i in r: if os.path.isdir(i): - shutil.rmtree(i) + try: + shutil.rmtree(i) + except: + sys.stderr.write('Warning: failed to delete '+i+'\n') else: os.remove(i) diff --git a/cldoc/generators/__init__.py b/cldoc/generators/__init__.py index 62ca739..cec512d 100644 --- a/cldoc/generators/__init__.py +++ b/cldoc/generators/__init__.py @@ -11,7 +11,5 @@ # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from .md import Md -from .search import Search -from .report import Report # vi:ts=4:et diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index 195a735..63ed1db 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -117,7 +117,10 @@ def ref_to_link(self,rf): link=relpath else: link=rf - return link.lower() + link=link.lower() + if link.find(".htm")==-1: + link+=".html" + return link def link_md(self,title, rf): lnk=self.ref_to_link(rf) @@ -242,7 +245,7 @@ def doc_method(self,f,elem): elif child.tag=='doc': doc=child.text #blank line before a heading h4: - f.write('\n### '+ret_type+' '+elem.attrib['name']) + f.write('\n### '+ret_type+' '+elem.attrib['name']) arglist=self.get_typed_arguments_text(elem) f.write('('+arglist+')') diff --git a/cldoc/tree.py b/cldoc/tree.py index 16e4624..9d19d98 100644 --- a/cldoc/tree.py +++ b/cldoc/tree.py @@ -59,7 +59,8 @@ def run(self): try: self.tu = self.index.parse(self.filename, self.flags, options=cindex.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD) - for inc in self.tu.get_includes(): + self.db=comment.CommentsDatabase(self.filename, self.tu, self.options) + """for inc in self.tu.get_includes(): filename = str(inc.include) self.includes[filename] = True @@ -72,7 +73,7 @@ def run(self): for e in self.extractfiles: if e in self.processed: continue - self.db=comment.CommentsDatabase(e, self.tu, self.options) + self.db=comment.CommentsDatabase(e, self.tu, self.options)""" except cindex.LibclangError as e: sys.stderr.write("\nError: Failed to parse.\n" + str(e) + "\n\n") diff --git a/scripts/test.py b/scripts/test.py index 771531d..286f944 100644 --- a/scripts/test.py +++ b/scripts/test.py @@ -16,13 +16,14 @@ print('VULKAN_SDK '+VULKAN_SDK) args=['C:/Simul/4.2/Simul/Simul.markdoc'] os.environ['INCLUDE']='' -source_dirs=['Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform'] +source_dirs=['Base','Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/PluginRenderInterface'] for d in source_dirs: dir=SIMUL+'/'+d+'/*.h' args.append(dir) #args.append('C:/Temp/UnityPluginInterface.h') -args.extend(['--merge',SIMUL+'/Help/*.md']) #,'--clean','C:/Simul/docs/ref' +args.extend(['--merge',SIMUL+'/Help/**/*.md']) +args.extend(['--clean','C:/Simul/docs/ref']) -#args.extend(['--post',SIMUL+'/Help/build.bat']) +args.extend(['--post',SIMUL+'/Help/build.bat']) print(' '.join(args)) cldoc.run_generate(args) \ No newline at end of file From 51f4f978f6dd0bce2912ee15a0993a1dd078d493 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 18 Jul 2019 17:11:18 +0100 Subject: [PATCH 28/32] Added base help directory, allowed for index and ref in required folders --- cldoc/documentmerger.py | 10 +++++----- scripts/test.py | 5 +++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cldoc/documentmerger.py b/cldoc/documentmerger.py index e4fa74b..5bc0611 100644 --- a/cldoc/documentmerger.py +++ b/cldoc/documentmerger.py @@ -78,8 +78,8 @@ def _split_categories(self, filename, contents): return [[c, ret[c],title[c],weight[c]] for c in ordered] def _normalized_qid(self, qid): - if qid == 'ref' or qid == 'index': - return None + #if qid == 'ref': #or qid == 'index': + #return None if qid.startswith('::'): return qid[2:] @@ -125,9 +125,9 @@ def _merge_file(self, mfilter, filename): qid = self._normalized_qid(parts[0]) - # 'ref' means the root of the reference: - if qid=='ref': - qid=None + # 'ref' means the root of the reference: ##removing to allow for an index in the ref folder + #if qid=='ref': + #qid=None if not self.qid_to_node[qid]: self.add_categories([[qid,cat_title]]) node = self.category_to_node[qid] diff --git a/scripts/test.py b/scripts/test.py index 286f944..f4d63f8 100644 --- a/scripts/test.py +++ b/scripts/test.py @@ -16,12 +16,13 @@ print('VULKAN_SDK '+VULKAN_SDK) args=['C:/Simul/4.2/Simul/Simul.markdoc'] os.environ['INCLUDE']='' -source_dirs=['Base','Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/PluginRenderInterface'] +source_dirs=['Base']#,'Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/PluginRenderInterface'] for d in source_dirs: dir=SIMUL+'/'+d+'/*.h' args.append(dir) #args.append('C:/Temp/UnityPluginInterface.h') -args.extend(['--merge',SIMUL+'/Help/**/*.md']) +args.extend(['--merge',SIMUL+'/Help/*.md']) +args.extend(['--merge',SIMUL+'/Help/**/*.md']) args.extend(['--clean','C:/Simul/docs/ref']) args.extend(['--post',SIMUL+'/Help/build.bat']) From 760b7cea95dc8821ffec7914c735d35eb9772ee7 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 19 Jul 2019 10:43:32 +0100 Subject: [PATCH 29/32] Added \layout command to cldoc --- cldoc/generators/md.py | 6 ++++++ cldoc/nodes/node.py | 2 ++ cldoc/parser.py | 5 ++++- cldoc/tree.py | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cldoc/generators/md.py b/cldoc/generators/md.py index 63ed1db..28fe59c 100644 --- a/cldoc/generators/md.py +++ b/cldoc/generators/md.py @@ -319,6 +319,10 @@ def write_md(self, node, fname): if 'weight' in elem.attrib: weight=elem.attrib['weight'] layout_name='reference' + if 'layout' in elem.attrib: + layout_name = elem.attrib['layout'] + + # if(elem.tag=='category'): #else: @@ -966,6 +970,8 @@ def node_to_md_ref(self, node): elem.set('name', props['name']) if 'weight' in props: elem.set('weight', props['weight']) + if 'layout' in props: + elem.set('layout', props['layout']) # can't put arbitrary text into brief, because markdown is replaced with html. # but we must make sure there's something there or it will be skipped diff --git a/cldoc/nodes/node.py b/cldoc/nodes/node.py index 4af1fa4..15b476c 100644 --- a/cldoc/nodes/node.py +++ b/cldoc/nodes/node.py @@ -52,6 +52,7 @@ def __init__(self, cursor, comment): self._refid = None self.slug= None self.weight = 0 + self.layout = '' self.sortid = 0 cls = self.__class__ @@ -307,6 +308,7 @@ def props(self): 'name': self.name, 'title': self.title, 'weight': self.weight, + 'layout': self.layout, } if self.is_anonymous: diff --git a/cldoc/parser.py b/cldoc/parser.py index 17f50b4..f87deba 100644 --- a/cldoc/parser.py +++ b/cldoc/parser.py @@ -243,6 +243,9 @@ def __init__(self): weight_k=backslash+Keyword('weight') weight=(weight_k+space.suppress()+integer).setParseAction(partial(self.parseDocumentProperty,1)) + layout_k=backslash+Keyword('layout') + layout=(layout_k+space.suppress()+(identifier|quoted_identifier)).setParseAction(partial(self.parseDocumentProperty,1)) + image_k=backslash+Keyword('image') image=(image_k.suppress()+space.suppress()+Keyword('html').suppress()+space.suppress()+(identifier|quoted_identifier)).setParseAction(partial(self.parseImage,1)) @@ -253,7 +256,7 @@ def __init__(self): unknown_command=(unknown_k).setParseAction(partial(self.parseUnknownCommand,1)) plainText=pt.setParseAction(partial(self.parsePlainText)) - command=(title|namespaces|ref|subpage|link|slug|em|a|param|_return|weight|image|git|unknown_command) + command=(title|namespaces|ref|subpage|link|slug|em|a|param|_return|weight|layout|image|git|unknown_command) bodyElement=(command|plainText) bodyLine = ( NotAny('@') + Group(ZeroOrMore(bodyElement)) + lineEnd).setParseAction(partial(self.parseTest,1)) diff --git a/cldoc/tree.py b/cldoc/tree.py index 9d19d98..0aaf2d4 100644 --- a/cldoc/tree.py +++ b/cldoc/tree.py @@ -481,6 +481,8 @@ def cross_ref_node(self, node): node.slug=val elif key=='weight': node.weight=val + elif key=='layout': + node.layout=val for child in node.children: self.cross_ref_node(child) From ec2a61b340ed6b24401490b4f81f336296ab6065 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 19 Jul 2019 13:23:04 +0100 Subject: [PATCH 30/32] Removed Debugging Comments --- scripts/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test.py b/scripts/test.py index f4d63f8..85698e7 100644 --- a/scripts/test.py +++ b/scripts/test.py @@ -16,7 +16,7 @@ print('VULKAN_SDK '+VULKAN_SDK) args=['C:/Simul/4.2/Simul/Simul.markdoc'] os.environ['INCLUDE']='' -source_dirs=['Base']#,'Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/PluginRenderInterface'] +source_dirs=['Base','Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/PluginRenderInterface'] for d in source_dirs: dir=SIMUL+'/'+d+'/*.h' args.append(dir) From c16ee9ee96c43b896ef1f45cbc26df6b6fe77930 Mon Sep 17 00:00:00 2001 From: James Date: Fri, 19 Jul 2019 14:16:35 +0100 Subject: [PATCH 31/32] Fixed File Name --- scripts/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test.py b/scripts/test.py index 85698e7..fc0b3f0 100644 --- a/scripts/test.py +++ b/scripts/test.py @@ -16,7 +16,7 @@ print('VULKAN_SDK '+VULKAN_SDK) args=['C:/Simul/4.2/Simul/Simul.markdoc'] os.environ['INCLUDE']='' -source_dirs=['Base','Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/PluginRenderInterface'] +source_dirs=['Base','Math','Geometry','Sky','Clouds','Terrain','Platform/CrossPlatform','Plugins/TrueSkyPluginRender'] for d in source_dirs: dir=SIMUL+'/'+d+'/*.h' args.append(dir) From 5529e178b4188633c2b1af2379505258fa823dc5 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 24 Oct 2019 16:27:23 +0100 Subject: [PATCH 32/32] Lower case image folder --- cldoc/cmdgenerate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cldoc/cmdgenerate.py b/cldoc/cmdgenerate.py index 1a28e6c..c900f97 100644 --- a/cldoc/cmdgenerate.py +++ b/cldoc/cmdgenerate.py @@ -131,7 +131,7 @@ def run(args): parser.add_argument('--post', default=None, metavar='POST', help='command to execute after completion') - parser.add_argument('--image-destination', default='Images', metavar='IMAGE_TARGET', + parser.add_argument('--image-destination', default='images', metavar='IMAGE_TARGET', help='Folder to put images in under output dir.') parser.add_argument('--image-path', default=[], metavar='IMAGE_PATHS', action='append',