diff options
author | Tomeu Vizoso <tomeu.vizoso@collabora.com> | 2012-02-20 18:18:04 +0100 |
---|---|---|
committer | Tomeu Vizoso <tomeu.vizoso@collabora.com> | 2012-02-20 18:18:04 +0100 |
commit | ad5c6abcbcd6c1cd26b9a714995c622f5dbe3b07 (patch) | |
tree | c040697e6ad1bb8269351968c3612927d9daea80 /giscanner | |
parent | 4fbb34bb0ea4a1d142052e62a29480c704550f8b (diff) | |
parent | 990b33b9fb5856b62550725bcff29f7e39a488a4 (diff) | |
download | gobject-introspection-ad5c6abcbcd6c1cd26b9a714995c622f5dbe3b07.tar.gz |
Merge branch 'mallard-templates'
Diffstat (limited to 'giscanner')
-rw-r--r-- | giscanner/ast.py | 2 | ||||
-rw-r--r-- | giscanner/docbookdescription.py | 185 | ||||
-rw-r--r-- | giscanner/docbookwriter.py | 569 | ||||
-rw-r--r-- | giscanner/docmain.py | 53 | ||||
-rw-r--r-- | giscanner/girparser.py | 30 | ||||
-rw-r--r-- | giscanner/mallard-C-class.tmpl | 48 | ||||
-rw-r--r-- | giscanner/mallard-C-default.tmpl | 11 | ||||
-rw-r--r-- | giscanner/mallard-C-function.tmpl | 95 | ||||
-rw-r--r-- | giscanner/mallard-C-namespace.tmpl | 19 | ||||
-rw-r--r-- | giscanner/mallard-C-property.tmpl | 13 | ||||
-rw-r--r-- | giscanner/mallard-C-record.tmpl | 12 | ||||
-rw-r--r-- | giscanner/mallard-C-signal.tmpl | 13 | ||||
-rw-r--r-- | giscanner/mallard-Python-class.tmpl | 63 | ||||
-rw-r--r-- | giscanner/mallard-Python-default.tmpl | 11 | ||||
-rw-r--r-- | giscanner/mallard-Python-enum.tmpl | 23 | ||||
-rw-r--r-- | giscanner/mallard-Python-function.tmpl | 88 | ||||
-rw-r--r-- | giscanner/mallard-Python-namespace.tmpl | 19 | ||||
-rw-r--r-- | giscanner/mallard-Python-property.tmpl | 16 | ||||
-rw-r--r-- | giscanner/mallard-Python-record.tmpl | 12 | ||||
-rw-r--r-- | giscanner/mallard-Python-signal.tmpl | 50 | ||||
-rw-r--r-- | giscanner/mallardwriter.py | 602 |
21 files changed, 700 insertions, 1234 deletions
diff --git a/giscanner/ast.py b/giscanner/ast.py index 91faff88..6df356dd 100644 --- a/giscanner/ast.py +++ b/giscanner/ast.py @@ -556,6 +556,7 @@ class Callable(Node): self.parameters = parameters self.throws = not not throws self.instance_parameter = None # Parameter + self.parent = None # A Class or Interface def get_parameter_index(self, name): for i, parameter in enumerate(self.parameters): @@ -1007,6 +1008,7 @@ class Property(Node): self.transfer = PARAM_TRANSFER_NONE else: self.transfer = transfer + self.parent = None # A Class or Interface class Callback(Callable): diff --git a/giscanner/docbookdescription.py b/giscanner/docbookdescription.py deleted file mode 100644 index 9ec7219d..00000000 --- a/giscanner/docbookdescription.py +++ /dev/null @@ -1,185 +0,0 @@ - -TAG_PROGRAM_LISTING = '<programlisting' -TAG_CDATA = '<![CDATA[' -TAGS = {TAG_PROGRAM_LISTING, TAG_CDATA, ']]>', '</programlisting>'} - -def get_formatted_description(description): - desc = description.replace("|[", "<informalexample><programlisting>") \ - .replace("]|", "</programlisting></informalexample>") - - desc = "<para>%s</para>" % desc - -# we still need to handle this case -# # Handle "#include <xxxxx>" -# $text =~ s/#include(\s+)<([^>]+)>/#include$1<$2>/g; - - formatted_desc = "" - - inside_tags = [] - last_offset = 0 - for start, end, tag in _find_xml_tag_matches(desc): - if len(inside_tags) == 0: - new_desc = "\n</para>\n<para>\n".join(desc[last_offset:start].split('\n\n')) - else: - new_desc = desc[last_offset:start] - - if TAG_CDATA not in inside_tags: - new_desc = _escape_non_cdata_section(new_desc) - - formatted_desc += new_desc - formatted_desc += tag - if tag == TAG_PROGRAM_LISTING: - formatted_desc += '>' - - if tag in (TAG_CDATA, TAG_PROGRAM_LISTING): - inside_tags.append(tag) - else: - try: - inside_tags.pop() - except IndexError: - print "Error: mismatched tag:", tag - last_offset = end - - formatted_desc += _escape_non_cdata_section(desc[last_offset:]) - return formatted_desc - -def _find_xml_tag_matches(string): - offset = 0 - while True: - indexes = [] - for tag in TAGS: - pos = string.find(tag, offset) - if pos != -1: - indexes.append((tag, pos)) - - if indexes: - tag, first = min(indexes, key=lambda x: x[1]) - if tag == TAG_PROGRAM_LISTING: - end = string.find('>', first + len(tag) - 1) + 1 - else: - end = first + len(tag) - offset = end - yield first, end, tag - else: - return - -def _escape_non_cdata_section(string): - string = _escape_ampersand_not_in_entity(string) - string = _escape_lt_not_in_xml_tag(string) - return _escape_gt_not_in_xml_tag(string) - -def _escape_ampersand_not_in_entity(string): - parts = string.split('&') - - output = parts[0] - for part in parts[1:]: - end = part.find(';') - if end == -1 or not part[:end].isalpha(): - output += "&" - else: - output += "&" - output += part - - return output - -def _is_valid_xml_tag_name(name): - if len(name) < 1: - return False - elif name.isalpha() or (name[0].isalpha() and name[1:].isalnum()): - return True - -def _is_valid_xml_tag(string): - # handle case where line end is between tag name and first argument. - # ie. <link\nlinkend="link-id">My Link</link> - string = string.replace('\n', ' ') - - if string[-1] == '/': - string = string[:-1] - - # string is the inner part of the tag, without < and > - if string[0] == '/' and _is_valid_xml_tag_name(string[1:]): - #valid end tag - return True - elif _is_valid_xml_tag_name(string): - #valid start tag with not params - return True - elif " " in string: - # we are looking for: <tagname arg="value" arg2="value2"> - # TODO: handle spaces in values (between quotations) - tagname, rest = string.split(" ", 1) - if not _is_valid_xml_tag_name(tagname): - return False - - while rest.strip(): - rest = rest.lstrip() - - if not '=' in rest: - return False - argname, rest = rest.split('=', 1) - if not _is_valid_xml_tag_name(argname): - return False - if rest[0] != '"': - return False - value, rest = rest[1:].split('"', 1) - - return True - -def _escape_lt_not_in_xml_tag(string): - parts = string.split('<') - - output = parts[0] - for part in parts[1:]: - end = part.find('>') - if end == -1 or not _is_valid_xml_tag(part[:end]): - output += "<" - else: - output += "<" - output += part - - return output - -def _escape_gt_not_in_xml_tag(string): - parts = string.split('>') - - output = parts[0] - for part in parts[1:]: - start = output.rfind('<') - if start == -1 or not _is_valid_xml_tag(output[start+1:]): - output += ">" - else: - output += ">" - output += part - - return output - - -def test(): - assert _is_valid_xml_tag_name('a') - assert _is_valid_xml_tag_name('refsect1') - assert not _is_valid_xml_tag_name('1refsect') - assert not _is_valid_xml_tag_name('1') - - assert _is_valid_xml_tag('/a') - assert _is_valid_xml_tag('/refsect1') - assert not _is_valid_xml_tag('/1') - assert _is_valid_xml_tag('link') - assert _is_valid_xml_tag('link linkend="value"') - assert _is_valid_xml_tag('link linkend="value"') - assert _is_valid_xml_tag('link/') - assert _is_valid_xml_tag('link linkend="value"/') - assert _is_valid_xml_tag('link linkend="value" arg23="anothervalue"') - assert _is_valid_xml_tag('link linkend="value" arg23="anothervalue with spaces"') - assert not _is_valid_xml_tag('link linkend="value arg23="anothervalue with spaces"') - assert not _is_valid_xml_tag('link linkend') - assert _is_valid_xml_tag('link\nlinkend="link-id"') - assert _is_valid_xml_tag('xref linkend="gtkstylecontext-classes"/') - - assert _is_valid_xml_tag('a href="http://www.gtk.org" title="<i>Our</i> website"') - assert _is_valid_xml_tag('ulink \nurl="http://www.freedesktop.org/Standards/wm-spec"') - - string = 'gtk_label_set_markup (label, "Go to the <a href="http://www.gtk.org" ' \ - + 'title="<i>Our</i> website">GTK+ website</a> for more...");' - assert _escape_lt_not_in_xml_tag(string) == string - -if __name__ == '__main__': - test() diff --git a/giscanner/docbookwriter.py b/giscanner/docbookwriter.py deleted file mode 100644 index d4701e68..00000000 --- a/giscanner/docbookwriter.py +++ /dev/null @@ -1,569 +0,0 @@ -#!/usr/bin/env python -# -*- Mode: Python -*- -# GObject-Introspection - a framework for introspecting GObject libraries -# Copyright (C) 2010 Zach Goldberg -# Copyright (C) 2011 Johan Dahlin -# -# This program 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; either version 2 -# of the License, or (at your option) any later version. -# -# 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. -# - -import sys - -from . import ast -from .girparser import GIRParser -from .xmlwriter import XMLWriter -from .docbookdescription import get_formatted_description - -XMLNS = "http://docbook.org/ns/docbook" -XMLVERSION = "5.0" -DOCTYPE = """<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" - "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ -<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'"> -<!ENTITY version SYSTEM "version.xml"> -]>""" #" - -def _space(num): - return " " * num - -class DocBookFormatter(object): - def __init__(self): - self.namespace = None - self.writer = None - - def set_namespace(self, namespace): - self.namespace = namespace - - def set_writer(self, writer): - self.writer = writer - - def get_type_string(self, type): - return str(type.ctype) - - def render_parameter(self, param_type, param_name): - return "%s %s" % (param_type, param_name) - - def _render_parameter(self, param, extra_content=''): - with self.writer.tagcontext("parameter"): - if param.type.ctype is not None: - link_dest = param.type.ctype.replace("*", "") - else: - link_dest = param.type.ctype - with self.writer.tagcontext("link", [("linkend", "%s" % link_dest)]): - self.writer.write_tag("type", [], link_dest) - self.writer.write_line(extra_content) - - def _render_parameters(self, parent, parameters): - self.writer.write_line( - "%s(" % _space(40 - len(parent.symbol))) - - parent_class = parent.parent_class - ctype = ast.Type(parent.parent_class.ctype + '*') - params = [] - params.append(ast.Parameter(parent_class.name.lower(), ctype)) - params.extend(parameters) - - first_param = True - for param in params: - if not first_param: - self.writer.write_line("\n%s" % _space(61)) - else: - first_param = False - - if not param == params[-1]: - comma = ", " - else: - comma = "" - - if isinstance(param.type, ast.Varargs): - with self.writer.tagcontext("parameter"): - self.writer.write_line('...%s' % comma) - else: - extra_content = " " - if param.type.ctype is not None and '*' in param.type.ctype: - extra_content += '*' - extra_content += param.argname - extra_content += comma - self._render_parameter(param, extra_content) - - self.writer.write_line(");\n") - - def get_method_as_title(self, method): - return "%s ()" % method.symbol - - def get_page_name(self, node): - # page name is only used for xml:id (not displayed to users) - if isinstance(node, ast.Alias) or node.gtype_name is None: - return node.ctype - return node.gtype_name - - def get_class_name(self, node): - if node.gtype_name is None: - return node.ctype - return node.gtype_name - - def get_type_name(self, node): - if isinstance(node, ast.Array): - if node.array_type == ast.Array.C: - return str(node.element_type) + "[]" - else: - return "%s<%s>" % (node.array_type, str(node.element_type)) - elif isinstance(node, ast.Map): - return "GHashTable<%s, %s>" % (str(node.key_type), str(node.value_type)) - elif isinstance(node, ast.List): - return "GList<%s>" % str(node.element_type) - else: - return str(node) - - def render_method(self, method, link=False): - self.writer.disable_whitespace() - - retval_type = method.retval.type - if retval_type.ctype: - link_dest = retval_type.ctype.replace("*", "") - else: - link_dest = str(retval_type) - - if retval_type.target_giname: - ns = retval_type.target_giname.split('.') - if ns[0] == self.namespace.name: - link_dest = "%s" % ( - retval_type.ctype.replace("*", "")) - - with self.writer.tagcontext("link", [("linkend", link_dest)]): - self.writer.write_tag("returnvalue", [], link_dest) - - if '*' in retval_type.ctype: - self.writer.write_line(' *') - - self.writer.write_line( - _space(20 - len(self.get_type_string(method.retval.type)))) - - if link: - self.writer.write_tag("link", [("linkend", - method.symbol.replace("_", "-"))], - method.symbol) - else: - self.writer.write_line(method.symbol) - - self._render_parameters(method, method.parameters) - self.writer.enable_whitespace() - - def _get_annotations(self, argument): - annotations = {} - - if hasattr(argument.type, 'element_type') and \ - argument.type.element_type is not None: - if isinstance(argument.type.element_type, ast.Array): - element_type = argument.type.element_type.array_type - else: - element_type = argument.type.element_type - annotations['element-type'] = element_type - - if argument.transfer is not None and argument.transfer != 'none': - annotations['transfer'] = argument.transfer - - if hasattr(argument, 'allow_none') and argument.allow_none: - annotations['allow-none'] = None - - return annotations - - def render_param_list(self, method): - self._render_param(method.parent_class.name.lower(), 'instance', []) - - for param in method.parameters: - if isinstance(param.type, ast.Varargs): - argname = '...' - else: - argname = param.argname - self._render_param(argname, param.doc, self._get_annotations(param)) - - self._render_param('Returns', method.retval.doc, - self._get_annotations(method.retval)) - - def _render_param(self, argname, doc, annotations): - with self.writer.tagcontext('varlistentry'): - with self.writer.tagcontext('term'): - self.writer.disable_whitespace() - try: - with self.writer.tagcontext('parameter'): - self.writer.write_line(argname) - if doc is not None: - self.writer.write_line(' :') - finally: - self.writer.enable_whitespace() - if doc is not None: - with self.writer.tagcontext('listitem'): - with self.writer.tagcontext('simpara'): - self.writer.write_line(doc) - if annotations: - with self.writer.tagcontext('emphasis', [('role', 'annotation')]): - for key, value in annotations.iteritems(): - self.writer.disable_whitespace() - try: - self.writer.write_line('[%s' % key) - if value is not None: - self.writer.write_line(' %s' % value) - self.writer.write_line(']') - finally: - self.writer.enable_whitespace() - - def render_property(self, prop, link=False): - prop_name = '"%s"' % prop.name - prop_type = self.get_type_name(prop.type) - - flags = [] - if prop.readable: - flags.append("Read") - if prop.writable: - flags.append("Write") - if prop.construct: - flags.append("Construct") - if prop.construct_only: - flags.append("Construct Only") - - self._render_prop_or_signal(prop_name, prop_type, flags) - - def _render_prop_or_signal(self, name, type_, flags): - self.writer.disable_whitespace() - - line = _space(2) + name + _space(27 - len(name)) - line += str(type_) + _space(22 - len(str(type_))) - line += ": " + " / ".join(flags) - - self.writer.write_line(line + "\n") - - self.writer.enable_whitespace() - - - def render_signal(self, signal, link=False): - sig_name = '"%s"' % signal.name - - flags = [] - if signal.when == "first": - flags.append("Run First") - elif signal.when == "last": - flags.append("Run Last") - elif signal.when == "cleanup": - flags.append("Cleanup") - - if signal.no_recurse: - flags.append('No Recursion') - if signal.detailed: - flags.append("Has Details") - if signal.action: - flags.append("Action") - if signal.no_hooks: - flags.append("No Hooks") - - self._render_prop_or_signal(sig_name, "", flags) - - -class DocBookFormatterPython(DocBookFormatter): - def get_title(self, page): - return "%s.%s" % (page.ast.namespace.name, page.ast.name) - - def render_struct(self, page): - class_ = page.ast - try: - self.writer.disable_whitespace() - self.writer.write_line("class %s" % self.get_title(page)) - - if hasattr(page.ast, "parent") and page.ast.parent is not None: - if isinstance(page.ast.parent, ast.Type): - parent_name = page.ast.parent - else: - parent_name = "%s.%s" % (page.ast.parent.namespace.name, - page.ast.parent.name) - elif isinstance(page.ast, ast.Interface): - parent_name = "GObject.Interface" - else: - parent_name = None - if parent_name is not None: - self.writer.write_line("(%s)" % (parent_name)) - - self.writer.write_line(":\n") - finally: - self.writer.enable_whitespace() - - -class DocBookFormatterC(DocBookFormatter): - def get_title(self, page): - return page.ast.ctype - - def render_struct(self, page): - try: - self.writer.disable_whitespace() - self.writer.write_line("struct ") - self.writer.write_tag( - "link", - [("linkend", "%s-struct" % page.name)], - "%s" % page.name) - self.writer.write_line(";\n") - finally: - self.writer.enable_whitespace() - - -class DocBookPage(object): - def __init__(self, name, ast_node): - self.methods = [] - self.properties = [] - self.signals = [] - self.name = name - self.description = ast_node.doc - self.ast = ast_node - self.id = None - - if isinstance(ast_node, (ast.Class, ast.Record, ast.Interface)): - for method in ast_node.methods: - method.parent_class = ast_node - self.methods = ast_node.methods - - if isinstance(ast_node, (ast.Class, ast.Interface)): - self.properties = ast_node.properties - self.signals = ast_node.signals - - def get_methods(self): - return self.methods - - def get_properties(self): - return self.properties - - def get_signals(self): - return self.signals - -class DocBookWriter(object): - def __init__(self, formatter): - self._namespace = None - self._pages = [] - - self._writer = XMLWriter() - - formatter.set_writer(self._writer) - self._formatter = formatter - - def _add_page(self, page): - self._pages.append(page) - - def add_transformer(self, transformer): - self._transformer = transformer - - self._namespace = self._transformer._namespace - self._formatter.set_namespace(self._namespace) - - for name, node in self._namespace.iteritems(): - if isinstance(node, (ast.Class, ast.Record, ast.Interface, ast.Alias)): - page_name = self._formatter.get_page_name(node) - self._add_node(node, page_name) - - def _add_node(self, node, name): - page = DocBookPage(name, node) - self._add_page(page) - - if isinstance(node, (ast.Class, ast.Record, ast.Interface, ast.Alias)): - page.id = node.ctype - - def write(self, output): - self._writer.write_line(DOCTYPE) - with self._writer.tagcontext("book", [ - ("xml:id", "page_%s" % self._namespace.name), - ("xmlns", XMLNS), - ("version", XMLVERSION)]): - self._writer.write_tag("title", [], "%s Documentation" % ( - self._namespace.name)) - - for page in self._pages: - self._render_page(page) - - fp = open(output, 'w') - fp.write(self._writer.get_xml()) - fp.close() - - def _render_page(self, page): - with self._writer.tagcontext("chapter", [("xml:id", "ch_%s" % ( - page.name))]): - self._writer.write_tag( - "title", [], self._formatter.get_title(page)) - - with self._writer.tagcontext("refsynopsisdiv", - [('id', '%s.synopsis' % page.name), - ('role', 'synopsis')]): - - self._writer.write_tag( - "title", [("role", "synopsis.title")], "Synopsis") - - if not isinstance(page.ast, ast.Alias): - self._writer.write_tag("anchor", [("id", page.name)]) - - with self._writer.tagcontext('synopsis'): - self._formatter.render_struct(page) - - for ast_node in page.get_methods(): - self._formatter.render_method(ast_node, link=True) - - if isinstance(page.ast, (ast.Class, ast.Interface)): - with self._writer.tagcontext("refsect1", - [('id', '%s.object-hierarchy' % page.name), - ('role', 'object_hierarchy')]): - self._writer.write_tag('title', [('role', 'object_hierarchy.title')], - "Object Hierarchy") - with self._writer.tagcontext('synopsis'): - self._render_page_object_hierarchy(page.ast) - - if page.get_properties(): - with self._writer.tagcontext('refsect1', - [('id', '%s.properties' % page.name), - ('role', 'properties')]): - self._writer.write_tag("title", [('role', 'properties.title')], - "Properties") - with self._writer.tagcontext("synopsis"): - for ast_node in page.get_properties(): - if isinstance(ast_node.type, ast.TypeUnknown): - print "Warning: ignoring property '%s' for " \ - "lack of type" % ast_node.name - continue - self._formatter.render_property(ast_node, link=True) - - if page.get_signals(): - with self._writer.tagcontext('refsect1', - [('id', '%s.signals' % page.name), - ('role', 'signal_proto')]): - self._writer.write_tag('title', [('role', 'signal_proto.title')], - "Signals") - with self._writer.tagcontext('synopsis'): - for ast_node in page.get_signals(): - self._formatter.render_signal(ast_node, link=True) - - if page.description: - with self._writer.tagcontext('refsect1', - [('id', '%s.description' % (page.name, ))]): - self._writer.write_tag( - "title", [("role", "desc.title")], "Description") - self._render_description(page.description) - - with self._writer.tagcontext('refsect1', - [('id', "%s-details" % page.id.lower()), - ("role", "details")]): - self._writer.write_tag("title", [("role", "details.title")], - "Details") - - if isinstance(page.ast, ast.Alias): - self._render_alias_detail(page.ast) - else: - self._render_struct_detail(page.ast) - - for ast_node in page.get_methods(): - self._render_method(ast_node) - - if page.get_properties(): - with self._writer.tagcontext('refsect1', - [('id', '%s.property-details' % page.name), - ('role', 'property_details')]): - self._writer.write_tag('title', [('role', 'property_details.title')], - "Property Details") - for ast_node in page.get_properties(): - self._render_property(ast_node) - - if page.get_signals(): - with self._writer.tagcontext('refsect1', - [('id', '%s.signal-details' % page.name), - ('role', 'signals')]): - self._writer.write_tag('title', [('role', 'signal.title')], - "Signal Details") - for ast_node in page.get_signals(): - self._render_signal(ast_node) - - def _render_alias_detail(self, alias): - with self._writer.tagcontext('refsect2', - [('id', "%s" % alias.ctype), - ('role', 'typedef')]): - self._writer.write_tag("title", [], "%s" % alias.ctype) - with self._writer.tagcontext("indexterm", [("zone", "%s" % alias.ctype)]): - self._writer.write_tag("primary", [("sortas", alias.name)], alias.ctype) - self._writer.write_tag("programlisting", - [], - "typedef %s %s" % (alias.target.ctype, - alias.ctype)) - self._writer.write_tag("para", [], alias.doc) - - def _render_struct_detail(self, struct): - with self._writer.tagcontext('refsect2', - [('id', "%s-struct" % struct.ctype), - ('role', 'struct')]): - self._writer.write_tag("title", [], "struct %s" % struct.ctype) - with self._writer.tagcontext("indexterm", [("zone", "%s-struct" % struct.ctype)]): - self._writer.write_tag("primary", [("sortas", struct.name)], struct.ctype) - self._writer.write_tag("programlisting", [], "struct %s;" % struct.ctype) - - def _render_method(self, ast_node): - - link_name = ast_node.symbol.replace("_", "-") - - self._writer.push_tag('refsect2', - [('id', link_name), - ('role', 'function')]) - self._writer.write_tag("title", [], - self._formatter.get_method_as_title(ast_node)) - - with self._writer.tagcontext("indexterm", [("zone", link_name)]): - self._writer.write_tag("primary", [], ast_node.name) - - with self._writer.tagcontext("programlisting"): - self._formatter.render_method(ast_node) - - description = ast_node.doc - if description: - self._render_description(ast_node.doc) - - with self._writer.tagcontext("variablelist", [("role", "params")]): - self._formatter.render_param_list(ast_node) - - self._writer.pop_tag() - - def _render_property(self, ast_node): - self._writer.write_line("Not implemented yet") - - def _render_signal(self, ast_node): - self._writer.write_line("Not implemented yet") - - def _render_page_object_hierarchy(self, page_node): - parent_chain = self._get_parent_chain(page_node) - parent_chain.append(page_node) - lines = [] - - for level, parent in enumerate(parent_chain): - prepend = "" - if level > 0: - prepend = _space((level - 1)* 6) + " +----" - lines.append(_space(2) + prepend + self._formatter.get_class_name(parent)) - - self._writer.disable_whitespace() - self._writer.write_line("\n".join(lines)) - self._writer.enable_whitespace() - - def _render_description(self, description): - formatted_desc = get_formatted_description(description) - self._writer.write_line(formatted_desc) - - def _get_parent_chain(self, page_node): - parent_chain = [] - - node = page_node - while node.parent and node.gi_name != 'GObject.Object': - node = self._transformer.lookup_giname(str(node.parent)) - parent_chain.append(node) - - parent_chain.reverse() - return parent_chain diff --git a/giscanner/docmain.py b/giscanner/docmain.py index e6ae8b60..e3ef4ae4 100644 --- a/giscanner/docmain.py +++ b/giscanner/docmain.py @@ -21,39 +21,15 @@ import os import optparse -from .docbookwriter import DocBookWriter -from .docbookwriter import DocBookFormatterC -from .docbookwriter import DocBookFormatterPython from .mallardwriter import MallardWriter -from .mallardwriter import MallardFormatterC -from .mallardwriter import MallardFormatterPython from .transformer import Transformer -class GIDocGenerator(object): - - def parse(self, filename): - if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ: - top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR'] - top_builddir = os.environ['UNINSTALLED_INTROSPECTION_BUILDDIR'] - extra_include_dirs = [os.path.join(top_srcdir, 'gir'), top_builddir] - else: - extra_include_dirs = [] - self.transformer = Transformer.parse_from_gir(filename, extra_include_dirs) - - def generate(self, writer, output): - writer.add_transformer(self.transformer) - writer.write(output) - def doc_main(args): parser = optparse.OptionParser('%prog [options] GIR-file') parser.add_option("-o", "--output", action="store", dest="output", help="Filename to write output") - parser.add_option("-f", "--format", - action="store", dest="format", - default="docbook", - help="Output format") parser.add_option("-l", "--language", action="store", dest="language", default="Python", @@ -66,28 +42,15 @@ def doc_main(args): if len(args) < 2: raise SystemExit("Need an input GIR filename") - if options.format == "docbook": - if options.language == "Python": - formatter = DocBookFormatterPython() - elif options.language == "C": - formatter = DocBookFormatterC() - else: - raise SystemExit("Unsupported language: %s" % (options.language, )) - writer = DocBookWriter(formatter) - elif options.format == "mallard": - if options.language == "Python": - formatter = MallardFormatterPython() - elif options.language == "C": - formatter = MallardFormatterC() - else: - raise SystemExit("Unsupported language: %s" % (options.language, )) - writer = MallardWriter(formatter) + if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ: + top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR'] + top_builddir = os.environ['UNINSTALLED_INTROSPECTION_BUILDDIR'] + extra_include_dirs = [os.path.join(top_srcdir, 'gir'), top_builddir] else: - raise SystemExit("Unsupported output format: %s" % (options.format, )) - - generator = GIDocGenerator() - generator.parse(args[1]) + extra_include_dirs = [] + transformer = Transformer.parse_from_gir(args[1], extra_include_dirs) - generator.generate(writer, options.output) + writer = MallardWriter(transformer, options.language) + writer.write(options.output) return 0 diff --git a/giscanner/girparser.py b/giscanner/girparser.py index 5faaf197..a59037da 100644 --- a/giscanner/girparser.py +++ b/giscanner/girparser.py @@ -256,27 +256,27 @@ class GIRParser(object): for iface in self._find_children(node, _corens('prerequisite')): obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name'])) for func_node in self._find_children(node, _corens('function')): - func = self._parse_function_common(func_node, ast.Function) + func = self._parse_function_common(func_node, ast.Function, obj) obj.static_methods.append(func) for method in self._find_children(node, _corens('method')): - func = self._parse_function_common(method, ast.Function) + func = self._parse_function_common(method, ast.Function, obj) func.is_method = True obj.methods.append(func) for method in self._find_children(node, _corens('virtual-method')): - func = self._parse_function_common(method, ast.VFunction) + func = self._parse_function_common(method, ast.VFunction, obj) self._parse_generic_attribs(method, func) func.is_method = True func.invoker = method.get('invoker') obj.virtual_methods.append(func) for ctor in self._find_children(node, _corens('constructor')): - func = self._parse_function_common(ctor, ast.Function) + func = self._parse_function_common(ctor, ast.Function, obj) func.is_constructor = True obj.constructors.append(func) obj.fields.extend(self._parse_fields(node)) for prop in self._find_children(node, _corens('property')): - obj.properties.append(self._parse_property(prop)) + obj.properties.append(self._parse_property(prop, obj)) for signal in self._find_children(node, _glibns('signal')): - obj.signals.append(self._parse_function_common(signal, ast.Signal)) + obj.signals.append(self._parse_function_common(signal, ast.Signal, obj)) def _parse_callback(self, node): callback = self._parse_function_common(node, ast.Callback) @@ -286,7 +286,7 @@ class GIRParser(object): function = self._parse_function_common(node, ast.Function) self._namespace.append(function) - def _parse_function_common(self, node, klass): + def _parse_function_common(self, node, klass, parent=None): name = node.attrib['name'] returnnode = node.find(_corens('return-value')) if not returnnode: @@ -319,6 +319,7 @@ class GIRParser(object): func.shadows = node.attrib.get('shadows', None) func.shadowed_by = node.attrib.get('shadowed-by', None) func.moved_to = node.attrib.get('moved-to', None) + func.parent = parent parameters_node = node.find(_corens('parameters')) if (parameters_node is not None): @@ -377,13 +378,13 @@ class GIRParser(object): compound.fields.extend(self._parse_fields(node)) for method in self._find_children(node, _corens('method')): compound.methods.append( - self._parse_function_common(method, ast.Function)) + self._parse_function_common(method, ast.Function, compound)) for func in self._find_children(node, _corens('function')): compound.static_methods.append( - self._parse_function_common(func, ast.Function)) + self._parse_function_common(func, ast.Function, compound)) for ctor in self._find_children(node, _corens('constructor')): compound.constructors.append( - self._parse_function_common(ctor, ast.Function)) + self._parse_function_common(ctor, ast.Function, compound)) return compound def _parse_record(self, node, anonymous=False): @@ -482,15 +483,15 @@ class GIRParser(object): if self._types_only: return for method in self._find_children(node, _corens('method')): - func = self._parse_function_common(method, ast.Function) + func = self._parse_function_common(method, ast.Function, obj) func.is_method = True obj.methods.append(func) for ctor in self._find_children(node, _corens('constructor')): obj.constructors.append( - self._parse_function_common(ctor, ast.Function)) + self._parse_function_common(ctor, ast.Function, obj)) for callback in self._find_children(node, _corens('callback')): obj.fields.append( - self._parse_function_common(callback, ast.Callback)) + self._parse_function_common(callback, ast.Callback, obj)) def _parse_field(self, node): type_node = None @@ -521,7 +522,7 @@ class GIRParser(object): self._parse_generic_attribs(node, field) return field - def _parse_property(self, node): + def _parse_property(self, node, parent): prop = ast.Property(node.attrib['name'], self._parse_type(node), node.attrib.get('readable') != '0', @@ -530,6 +531,7 @@ class GIRParser(object): node.attrib.get('construct-only') == '1', node.attrib.get('transfer-ownership')) self._parse_generic_attribs(node, prop) + prop.parent = parent return prop def _parse_member(self, node): diff --git a/giscanner/mallard-C-class.tmpl b/giscanner/mallard-C-class.tmpl new file mode 100644 index 00000000..2d739043 --- /dev/null +++ b/giscanner/mallard-C-class.tmpl @@ -0,0 +1,48 @@ +<?xml version="1.0"?> +<page id="${node.namespace.name}.${node.name}" + type="guide" + style="class" + xmlns="http://projectmallard.org/1.0/" + xmlns:api="http://projectmallard.org/experimental/api/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="index" group="class"/> + </info> + <title>${node.ctype}</title> +${formatter.format(node.doc)} +% if node.version: +<p>Since ${node.version}</p> +% endif + <synopsis ui:expanded="no"> + <title>Hierarchy</title> + <tree> + <item> + <code>GObjectObject</code> + </item> + </tree> + </synopsis> + <links type="topic" ui:expanded="yes" + api:type="function" api:mime="text/x-csrc" + groups="constructor" style="linklist"> + <title>Constructors</title> + </links> + <links type="topic" ui:expanded="yes" + api:type="function" api:mime="text/x-csrc" + groups="method" style="linklist"> + <title>Methods</title> + </links> + <links type="topic" ui:expanded="yes" + api:type="function" api:mime="text/x-csrc" + groups="function" style="linklist"> + <title>Functions</title> + </links> + <links type="topic" ui:expanded="yes" groups="property" style="linklist"> + <title>Properties</title> + </links> + <links type="topic" ui:expanded="yes" groups="signal" style="linklist"> + <title>Signals</title> + </links> + <links type="topic" ui:expanded="yes" groups="#first #default #last" style="linklist"> + <title>Other</title> + </links> +</page> diff --git a/giscanner/mallard-C-default.tmpl b/giscanner/mallard-C-default.tmpl new file mode 100644 index 00000000..577fa566 --- /dev/null +++ b/giscanner/mallard-C-default.tmpl @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<page id="${namespace.name}.${node.name}" + type="topic" + style="" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + </info> + <title>${namespace.name}.${node.name}</title> +${formatter.format(node.doc)} +</page> diff --git a/giscanner/mallard-C-function.tmpl b/giscanner/mallard-C-function.tmpl new file mode 100644 index 00000000..2da4710f --- /dev/null +++ b/giscanner/mallard-C-function.tmpl @@ -0,0 +1,95 @@ +<?xml version="1.0"?> +<% +page_style = 'function' +if node.is_constructor: + page_style = 'constructor' +elif node.is_method: + page_style = 'method' +%> +<page id="${page_id}" + type="topic" + style="${page_style}" + xmlns="http://projectmallard.org/1.0/" + xmlns:api="http://projectmallard.org/experimental/api/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> +% if node.parent is not None: + <link type="guide" xref="${namespace.name}.${node.parent.name}" group="${page_style}"/> +% else: + <link type="guide" xref="index" group="${page_style}"/> +% endif + <api:function> + <api:returns> + <api:type>${formatter.format_type(node.retval.type) | x}</api:type> + </api:returns> + <api:name>${node.symbol}</api:name> +% if node.is_method: + <api:arg> + <api:type>${node.parent.ctype} *</api:type> + <api:name>self</api:name> + </api:arg> +% endif +% for arg in node.parameters: +% if arg.type.ctype == '<varargs>': + <api:varargs/> +% else: + <api:arg> + <api:type>${formatter.format_type(arg.type) | x}</api:type> + <api:name>${arg.argname}</api:name> + </api:arg> +% endif +% endfor + </api:function> + </info> + <title>${node.symbol}</title> +<synopsis><code mime="text/x-csrc"> +${node.retval.type.ctype} ${node.symbol} (\ +% if node.is_method: +${node.parent.ctype} *self\ +%endif +% if len(node.parameters) == 0: +% if not node.is_method: +void\ +%endif +); +% elif node.is_method: +, +% endif +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +% if ix != 0: +${' ' * (len(formatter.format_type(node.retval.type)) + len(node.symbol) + 3)}\ +% endif +% if arg.type.ctype == '<varargs>': +...\ +% else: +${formatter.format_type(arg.type) | x} ${arg.argname}\ +% endif +% if ix == len(node.parameters) - 1: +); +% else: +, +%endif +% endfor +</code></synopsis> +${formatter.format(node.doc)} + +% if node.parameters or node.retval: +<table> +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +<tr> +<td><p>${arg.argname} :</p></td> +<td>${formatter.format(arg.doc)}</td> +</tr> +% endfor +% if node.retval: +<tr> +<td><p>Returns :</p></td> +<td>${formatter.format(node.retval.doc)}</td> +</tr> +% endif +</table> +% endif +% if node.version: +<p>Since ${node.version}</p> +% endif +</page> diff --git a/giscanner/mallard-C-namespace.tmpl b/giscanner/mallard-C-namespace.tmpl new file mode 100644 index 00000000..284ba238 --- /dev/null +++ b/giscanner/mallard-C-namespace.tmpl @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<page id="index" + type="guide" + style="namespace" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + </info> + <title>${node.name} Documentation</title> + <links type="topic" ui:expanded="yes" groups="class" style="linklist"> + <title>Classes</title> + </links> + <links type="topic" ui:expanded="yes" groups="function" style="linklist"> + <title>Functions</title> + </links> + <links type="topic" ui:expanded="yes" groups="#first #default #last" style="linklist"> + <title>Other</title> + </links> +</page> diff --git a/giscanner/mallard-C-property.tmpl b/giscanner/mallard-C-property.tmpl new file mode 100644 index 00000000..2d37ba10 --- /dev/null +++ b/giscanner/mallard-C-property.tmpl @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<page id="${namespace.name}.${node.name}" + type="topic" + style="property" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="${namespace.name}.${node.parent.name}" group="property"/> + <title type="link" role="topic">${node.name}</title> + </info> + <title>${node.parent.ctype}:${node.name}</title> +${formatter.format(node.doc)} +</page> diff --git a/giscanner/mallard-C-record.tmpl b/giscanner/mallard-C-record.tmpl new file mode 100644 index 00000000..a173e77a --- /dev/null +++ b/giscanner/mallard-C-record.tmpl @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<page id="${node.namespace.name}.${node.name}" + type="guide" + style="record" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="index"/> + </info> + <title>${node.namespace.name}${node.name}</title> +${formatter.format(node.doc)} +</page> diff --git a/giscanner/mallard-C-signal.tmpl b/giscanner/mallard-C-signal.tmpl new file mode 100644 index 00000000..7aae3ae4 --- /dev/null +++ b/giscanner/mallard-C-signal.tmpl @@ -0,0 +1,13 @@ +<?xml version="1.0"?> +<page id="${namespace.name}.${node.name}" + type="topic" + style="signal" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="${namespace.name}.${node.parent.name}" group="signal"/> + <title type="link" role="topic">${node.name}</title> + </info> + <title>${node.parent.ctype}::${node.name}</title> +${formatter.format(node.doc)} +</page> diff --git a/giscanner/mallard-Python-class.tmpl b/giscanner/mallard-Python-class.tmpl new file mode 100644 index 00000000..62feb9ab --- /dev/null +++ b/giscanner/mallard-Python-class.tmpl @@ -0,0 +1,63 @@ +<?xml version="1.0"?> +<page id="${node.namespace.name}.${node.name}" + type="guide" + style="class" + xmlns="http://projectmallard.org/1.0/" + xmlns:api="http://projectmallard.org/experimental/api/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="index" group="class"/> + </info> + <title>${namespace.name}.${node.name}</title> +${formatter.format(node.doc)} + + <synopsis><code> +from gi.repository import ${namespace.name} + +${formatter.to_underscores(node.name).lower()} = ${namespace.name}.${node.name}(\ +% for property_, ix in zip(node.properties, range(len(node.properties))): +% if property_.construct or property_.construct_only or property_.writable: +<link xref='${namespace.name}.${node.name}-${property_.name}'>${property_.name.replace('-', '_')}</link>=value\ +% if ix != len(node.properties) - 1: +, \ +% endif +% endif +% endfor +)\ + </code></synopsis> + +% if node.version: +<p>Since ${node.version}</p> +% endif + <synopsis> + <title>Hierarchy</title> + <tree> +% for class_ in formatter.get_class_hierarchy(node): + <item> + <code>${class_.namespace.name}.${class_.name}</code> +% endfor +% for class_ in formatter.get_class_hierarchy(node): + </item> +% endfor + </tree> + </synopsis> + <links type="topic" ui:expanded="yes" + api:type="function" api:mime="text/x-python" + groups="method" style="linklist"> + <title>Methods</title> + </links> + <links type="topic" ui:expanded="yes" + api:type="function" api:mime="text/x-python" + groups="function" style="linklist"> + <title>Functions</title> + </links> + <links type="topic" ui:expanded="yes" groups="property" style="linklist"> + <title>Properties</title> + </links> + <links type="topic" ui:expanded="yes" groups="signal" style="linklist"> + <title>Signals</title> + </links> + <links type="topic" ui:expanded="yes" groups="#first #default #last" style="linklist"> + <title>Other</title> + </links> +</page> diff --git a/giscanner/mallard-Python-default.tmpl b/giscanner/mallard-Python-default.tmpl new file mode 100644 index 00000000..683adf6a --- /dev/null +++ b/giscanner/mallard-Python-default.tmpl @@ -0,0 +1,11 @@ +<?xml version="1.0"?> +<page id="${page_id}" + type="topic" + style="" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + </info> + <title>${namespace.name}.${node.name}</title> +${formatter.format(node.doc)} +</page> diff --git a/giscanner/mallard-Python-enum.tmpl b/giscanner/mallard-Python-enum.tmpl new file mode 100644 index 00000000..fd6ca0fb --- /dev/null +++ b/giscanner/mallard-Python-enum.tmpl @@ -0,0 +1,23 @@ +<?xml version="1.0"?> +<page id="${node.namespace.name}.${node.name}" + type="guide" + style="enum" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="index"/> + </info> + <title>${node.namespace.name}.${node.name}</title> + ${formatter.format(node.doc)} +% if node.members: +<table> +% for member, ix in zip(node.members, range(len(node.members))): +<tr> +<td><p>${node.name}.${member.name.upper()} :</p></td> +<td>${formatter.format(member.doc)}</td> +</tr> +% endfor +</table> +% endif + +</page> diff --git a/giscanner/mallard-Python-function.tmpl b/giscanner/mallard-Python-function.tmpl new file mode 100644 index 00000000..7aa25e8e --- /dev/null +++ b/giscanner/mallard-Python-function.tmpl @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<% +page_style = 'function' +if node.is_constructor: + page_style = 'constructor' +elif node.is_method: + page_style = 'method' +%> +<page id="${page_id}" + type="topic" + style="${page_style}" + xmlns="http://projectmallard.org/1.0/" + xmlns:api="http://projectmallard.org/experimental/api/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> +% if node.parent is not None: + <link type="guide" xref="${namespace.name}.${node.parent.name}" group="${page_style}"/> +% else: + <link type="guide" xref="index" group="${page_style}"/> +% endif + <api:function> + <api:returns> + <api:type>${formatter.format_type(node.retval.type) | x}</api:type> + </api:returns> + <api:name>${node.symbol}</api:name> +% if node.is_method: + <api:arg> + <api:type>${node.parent.ctype} *</api:type> + <api:name>self</api:name> + </api:arg> +% endif +% for arg in node.parameters: +% if arg.type.ctype == '<varargs>': + <api:varargs/> +% else: + <api:arg> + <api:type>${formatter.format_type(arg.type) | x}</api:type> + <api:name>${arg.argname}</api:name> + </api:arg> +% endif +% endfor + </api:function> + </info> + <title>${node.name}</title> +<synopsis><code mime="text/x-python"> +% if len(node.parameters) != 0: +@accepts(\ +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +${formatter.format_type(arg.type) | x}\ +% if ix != len(node.parameters) - 1: +, \ +%endif +% endfor +) +% endif +@returns(${formatter.format_type(node.retval.type) | x}) +def \ +${node.name}(\ +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +${arg.argname}\ +% if ix != len(node.parameters) - 1: +, \ +%endif +% endfor +) +</code></synopsis> +${formatter.format(node.doc)} + +% if node.parameters or node.retval: +<table> +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +<tr> +<td><p>${arg.argname} :</p></td> +<td>${formatter.format(arg.doc)}</td> +</tr> +% endfor +% if node.retval and node.retval.type.ctype != 'void': +<tr> +<td><p>Returns :</p></td> +<td>${formatter.format(node.retval.doc)}</td> +</tr> +% endif +</table> +% endif +% if node.version: +<p>Since ${node.version}</p> +% endif +</page> diff --git a/giscanner/mallard-Python-namespace.tmpl b/giscanner/mallard-Python-namespace.tmpl new file mode 100644 index 00000000..935cd440 --- /dev/null +++ b/giscanner/mallard-Python-namespace.tmpl @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<page id="index" + type="guide" + style="namespace" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + </info> + <title>${node.name} Documentation</title> + <links type="topic" ui:expanded="yes" groups="class"> + <title>Classes</title> + </links> + <links type="topic" ui:expanded="yes" groups="function"> + <title>Functions</title> + </links> + <links type="topic" ui:expanded="yes" groups="#first #default #last"> + <title>Other</title> + </links> +</page> diff --git a/giscanner/mallard-Python-property.tmpl b/giscanner/mallard-Python-property.tmpl new file mode 100644 index 00000000..c4d2229e --- /dev/null +++ b/giscanner/mallard-Python-property.tmpl @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<page id="${namespace.name}.${node.parent.name}-${node.name}" + type="topic" + style="property" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="${namespace.name}.${node.parent.name}" group="property"/> + <title type="link" role="topic">${node.name}</title> + </info> + <title>${namespace.name}.${node.parent.name}:${node.name}</title> +<synopsis><code mime="text/x-python"> +"${node.name}" ${formatter.format_type(node.type)} : ${formatter.format_property_flags(node)} +</code></synopsis> +${formatter.format(node.doc)} +</page> diff --git a/giscanner/mallard-Python-record.tmpl b/giscanner/mallard-Python-record.tmpl new file mode 100644 index 00000000..1b00e3be --- /dev/null +++ b/giscanner/mallard-Python-record.tmpl @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<page id="${node.namespace.name}.${node.name}" + type="guide" + style="record" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="index"/> + </info> + <title>${node.namespace.name}${node.name}</title> + <p>${node.doc}</p> +</page> diff --git a/giscanner/mallard-Python-signal.tmpl b/giscanner/mallard-Python-signal.tmpl new file mode 100644 index 00000000..7dcbb0cd --- /dev/null +++ b/giscanner/mallard-Python-signal.tmpl @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<page id="${namespace.name}.${node.parent.name}-${node.name}" + type="topic" + style="signal" + xmlns="http://projectmallard.org/1.0/" + xmlns:ui="http://projectmallard.org/experimental/ui/"> + <info> + <link type="guide" xref="${namespace.name}.${node.parent.name}" group="signal"/> + <title type="link" role="topic">${node.name}</title> + </info> + <title>${namespace.name}.${node.parent.name}::${node.name}</title> +<synopsis><code mime="text/x-python"> +def callback(\ +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +${arg.argname}, \ +% endfor +user_param1, ...) +</code></synopsis> +${formatter.format(node.doc)} + +% if node.parameters or node.retval: +<table> +% for arg, ix in zip(node.parameters, range(len(node.parameters))): +<tr> +<td><p>${arg.argname} :</p></td> +<td>${formatter.format(arg.doc)}</td> +</tr> +% endfor +<tr> +<td><p>user_param1 :</p></td> +<td><p>first user parameter (if any) specified with the connect() method</p></td> +</tr> +<tr> +<td><p>... :</p></td> +<td><p>additional user parameters (if any)</p></td> +</tr> +% if node.retval and \ + node.retval.type.ctype != 'void' and \ + node.retval.type.ctype is not None: +<tr> +<td><p>Returns :</p></td> +<td>${node.retval.type.ctype} ${formatter.format(node.retval.doc)}</td> +</tr> +% endif +</table> +% endif +% if node.version: +<p>Since ${node.version}</p> +% endif +</page> diff --git a/giscanner/mallardwriter.py b/giscanner/mallardwriter.py index a71528e9..5fd97048 100644 --- a/giscanner/mallardwriter.py +++ b/giscanner/mallardwriter.py @@ -21,465 +21,225 @@ # 02110-1301, USA. # -import os.path +import os import re -import sys +import tempfile -from . import ast -from .girparser import GIRParser -from .xmlwriter import XMLWriter +from xml.sax import saxutils +from mako.template import Template +from mako.runtime import supports_caller -XMLNS = "http://projectmallard.org/1.0/" -XMLNS_UI = "http://projectmallard.org/experimental/ui/" +from . import ast +from .utils import to_underscores def _space(num): return " " * num class MallardFormatter(object): - def __init__(self): - pass - - def get_title(self, node, parent): - raise NotImplementedError('get_title not implemented') - - # FIXME - def render_parameter(self, param_type, param_name): - return "%s %s" % (param_type, param_name) - - def _render_parameter(self, param, extra_content=''): - with self._writer.tagcontext("parameter"): - if param.type.ctype is not None: - link_dest = param.type.ctype.replace("*", "") - else: - link_dest = param.type.ctype - with self._writer.tagcontext("link", [("linkend", "%s" % link_dest)]): - self._writer.write_tag("type", [], link_dest) - self._writer.write_line(extra_content) - - def _render_parameters(self, parent, parameters): - self._writer.write_line( - "%s(" % _space(40 - len(parent.symbol))) - - parent_class = parent.parent_class - ctype = ast.Type(parent.parent_class.ctype + '*') - params = [] - params.append(ast.Parameter(parent_class.name.lower(), ctype)) - params.extend(parameters) - - first_param = True - for param in params: - if not first_param: - self._writer.write_line("\n%s" % _space(61)) - else: - first_param = False - - if not param == params[-1]: - comma = ", " - else: - comma = "" - if param.type.target_fundamental == '<varargs>': - extra_content = "..." - continue - extra_content = " " - if param.type.ctype is not None and '*' in param.type.ctype: - extra_content += '*' - if param.argname is None: - import pdb - pdb.set_trace() - extra_content += param.argname - extra_content += comma - self._render_parameter(param, extra_content) + def __init__(self, transformer): + self._transformer = transformer - self._writer.write_line(");\n") + def escape(self, text): + return saxutils.escape(text.encode('utf-8')).decode('utf-8') - def get_method_as_title(self, entity): - method = entity.get_ast() - return "%s ()" % method.symbol + def format(self, doc): + if doc is None: + return '' - def get_page_name(self, node): - if node.gtype_name is None: - return node.ctype - return node.gtype_name + result = '' + for para in doc.split('\n\n'): + result += '<p>' + result += self.format_inline(para) + result += '</p>' + return result - def get_class_name(self, node): - if node.gtype_name is None: - return node.ctype - return node.gtype_name + def format_inline(self, para): + result = '' - def get_type_name(self, node): - if isinstance(node, ast.Array): - if node.array_type == ast.Array.C: - return str(node.element_type) + "[]" + poss = [] + poss.append((para.find('#'), '#')) + poss = [pos for pos in poss if pos[0] >= 0] + poss.sort(cmp=lambda x, y: cmp(x[0], y[0])) + if len(poss) == 0: + result += self.escape(para) + elif poss[0][1] == '#': + pos = poss[0][0] + result += self.escape(para[:pos]) + rest = para[pos + 1:] + link = re.split('[^a-zA-Z_:-]', rest, maxsplit=1)[0] + if link.endswith(':'): + link = link[:-1] + namespace = self._transformer.namespace + if '::' in link: + type_name, signal_name = link.split('::') + if type_name in namespace.ctypes: + type_ = namespace.get_by_ctype(type_name) + xref = '%s.%s-%s' % (namespace.name, type_.name, signal_name) + xref_name = '%s.%s::%s' % (namespace.name, type_.name, signal_name) + else: + xref = link + xref_name = link + elif ':' in link: + type_name, property_name = link.split(':') + if type_name in namespace.ctypes: + type_ = namespace.get_by_ctype(type_name) + xref = '%s.%s-%s' % (namespace.name, type_.name, property_name) + xref_name = '%s.%s:%s' % (namespace.name, type_.name, property_name) + else: + xref = link + xref_name = link + elif link in namespace.ctypes: + type_ = namespace.get_by_ctype(link) + xref = '%s.%s' % (namespace.name, type_.name) + xref_name = xref else: - return "%s<%s>" % (node.array_type, str(node.element_type)) - elif isinstance(node, ast.Map): - return "GHashTable<%s, %s>" % (str(node.key_type), str(node.value_type)) - elif isinstance(node, ast.List): - return "GList<%s>" % str(node.element_type) - else: - return str(node) - - def render_method(self, entity, link=False): - method = entity.get_ast() - self._writer.disable_whitespace() - - retval_type = method.retval.type - if retval_type.ctype: - link_dest = retval_type.ctype.replace("*", "") - else: - link_dest = str(retval_type) - - if retval_type.target_giname: - ns = retval_type.target_giname.split('.') - if ns[0] == self._namespace.name: - link_dest = "%s" % ( - retval_type.ctype.replace("*", "")) - - with self._writer.tagcontext("link", [("linkend", link_dest)]): - self._writer.write_tag("returnvalue", [], link_dest) - - if retval_type.ctype is not None and '*' in retval_type.ctype: - self._writer.write_line(' *') - - self._writer.write_line( - _space(20 - len(self.get_type_string(method.retval.type)))) - - if link: - self._writer.write_tag("link", [("linkend", - method.symbol.replace("_", "-"))], - method.symbol) - else: - self._writer.write_line(method.symbol) - - self._render_parameters(method, method.parameters) - self._writer.enable_whitespace() - - def _get_annotations(self, argument): - annotations = {} - - if hasattr(argument.type, 'element_type') and \ - argument.type.element_type is not None: - annotations['element-type'] = argument.type.element_type - - if argument.transfer is not None and argument.transfer != 'none': - annotations['transfer'] = argument.transfer - - if hasattr(argument, 'allow_none') and argument.allow_none: - annotations['allow-none'] = None - - return annotations - - def render_param_list(self, entity): - method = entity.get_ast() - - self._render_param(method.parent_class.name.lower(), 'instance', []) - - for param in method.parameters: - self._render_param(param.argname, param.doc, - self._get_annotations(param)) - - self._render_param('Returns', method.retval.doc, - self._get_annotations(method.retval)) - - def _render_param(self, argname, doc, annotations): - if argname is None: - return - with self._writer.tagcontext('varlistentry'): - with self._writer.tagcontext('term'): - self._writer.disable_whitespace() - try: - with self._writer.tagcontext('parameter'): - self._writer.write_line(argname) - if doc is not None: - self._writer.write_line(' :') - finally: - self._writer.enable_whitespace() - if doc is not None: - with self._writer.tagcontext('listitem'): - with self._writer.tagcontext('simpara'): - self._writer.write_line(doc) - if annotations: - with self._writer.tagcontext('emphasis', [('role', 'annotation')]): - for key, value in annotations.iteritems(): - self._writer.disable_whitespace() - try: - self._writer.write_line('[%s' % key) - if value is not None: - self._writer.write_line(' %s' % value) - self._writer.write_line(']') - finally: - self._writer.enable_whitespace() + xref = link + xref_name = link + result += '<link xref="%s">%s</link>' % (xref, xref_name) + if len(link) < len(rest): + result += self.format_inline(rest[len(link):]) - def render_property(self, entity, link=False): - prop = entity.get_ast() + return result - prop_name = '"%s"' % prop.name - prop_type = self.get_type_name(prop.type) + def format_type(self, type_): + raise NotImplementedError + def format_property_flags(self, property_): flags = [] - if prop.readable: + if property_.readable: flags.append("Read") - if prop.writable: + if property_.writable: flags.append("Write") - if prop.construct: + if property_.construct: flags.append("Construct") - if prop.construct_only: + if property_.construct_only: flags.append("Construct Only") - self._render_prop_or_signal(prop_name, prop_type, flags) - - def _render_prop_or_signal(self, name, type_, flags): - self._writer.disable_whitespace() - - line = _space(2) + name + _space(27 - len(name)) - line += str(type_) + _space(22 - len(str(type_))) - line += ": " + " / ".join(flags) + return " / ".join(flags) - self._writer.write_line(line + "\n") + def to_underscores(self, string): + return to_underscores(string) - self._writer.enable_whitespace() + def get_class_hierarchy(self, node): + parent_chain = [] + while node.parent: + node = self._transformer.lookup_giname(str(node.parent)) + parent_chain.append(node) - def render_signal(self, entity, link=False): - signal = entity.get_ast() - - sig_name = '"%s"' % signal.name - flags = ["TODO: signal flags not in GIR currently"] - self._render_prop_or_signal(sig_name, "", flags) + parent_chain.reverse() + return parent_chain class MallardFormatterC(MallardFormatter): - def get_title(self, node, parent): - if isinstance(node, ast.Namespace): - return "%s Documentation" % node.name - elif isinstance(node, ast.Function): - return node.symbol - elif isinstance(node, ast.Property): - return parent.c_name + ':' + node.name - elif isinstance(node, ast.Signal): - return parent.c_name + '::' + node.name - else: - return node.c_name -class MallardFormatterPython(MallardFormatter): - def get_title(self, node, parent): - if isinstance(node, ast.Namespace): - return "%s Documentation" % node.name - elif isinstance(node, ast.Function): - if node.is_method or node.is_constructor: - return "%s.%s.%s" % (node.namespace.name, parent.name, node.name) - else: - return "%s.%s" % (node.namespace.name, node.name) - elif isinstance(node, ast.Property): - return "%s" % node.name - elif isinstance(node, ast.Signal): - return "%s" % node.name + def format_type(self, type_): + if isinstance(type_, ast.Array): + try: + return self.format_type(type_.element_type) + '*' + except: + return type_.target_fundamental + elif type_.ctype is not None: + return type_.ctype else: - return "%s.%s" % (node.namespace.name, node.name) - -class MallardPage(object): - def __init__(self, writer, node, parent): - self.writer = writer - self.node = node - self.parent = parent - self.page_id = None - self.page_type = 'topic' - self.page_style = '' + return type_.target_fundamental - node.page = self - if not isinstance(node, ast.Namespace): - if node.namespace is None: - if parent is not None and parent.namespace is not None: - node.namespace = parent.namespace - - self.title = writer._formatter.get_title(node, parent) - self.links = [] - self.linksels = [] +class MallardFormatterPython(MallardFormatter): - if isinstance(node, ast.Namespace): - self.page_id = 'index' - elif isinstance(node, ast.Property) and parent is not None: - self.page_id = node.namespace.name + '.' + parent.name + '-' + node.name - elif isinstance(node, ast.Signal) and parent is not None: - self.page_id = node.namespace.name + '.' + parent.name + '--' + node.name - elif parent is not None and not isinstance(parent, ast.Namespace): - self.page_id = node.namespace.name + '.' + parent.name + '.' + node.name + def format_type(self, type_): + if isinstance(type_, ast.Array): + return '[' + self.format_type(type_.element_type) + ']' + elif isinstance(type_, ast.Map): + return '{%s: %s}' % (self.format_type(type_.key_type), + self.format_type(type_.value_type)) + elif type_.target_giname is not None: + return type_.target_giname else: - self.page_id = node.namespace.name + '.' + node.name - - if getattr(node, 'symbol', None) is not None: - self.writer._xrefs[node.symbol] = self.page_id - elif isinstance(node, ast.Class): - self.writer._xrefs[node.c_name] = self.page_id - - self.create_content() - self.add_child_nodes() - - def add_link(self, linktype, xref, group=None): - self.links.append((linktype, xref, group)) - - def add_child_nodes(self): - children = [] - if isinstance(self.node, ast.Namespace): - children = [node for node in self.node.itervalues()] - elif isinstance(self.node, (ast.Class, ast.Record)): - children = self.node.methods + self.node.constructors - elif isinstance(self.node, ast.Interface): - children = self.node.methods - - if isinstance(self.node, (ast.Class, ast.Interface)): - children += self.node.properties + self.node.signals - for child in children: - self.writer._pages.append(MallardPage(self.writer, child, self.node)) + return type_.target_fundamental - def create_content(self): - if isinstance(self.node, ast.Namespace): - self.page_type = 'guide' - self.page_style = 'namespace' - self.linksels = (('class', 'Classes'), - ('function', 'Functions'), - ('#first #default #last', 'Other')) - elif isinstance(self.node, ast.Class): - self.page_type = 'guide' - self.page_style = 'class' - self.linksels = (('constructor', 'Constructors'), - ('method', 'Methods'), - ('property', 'Properties'), - ('signal', 'Signals'), - ('#first #default #last', 'Other')) - self.add_link('guide', self.parent.page.page_id, 'class') - elif isinstance(self.node, ast.Record): - self.page_type = 'guide' - self.page_style = 'record' - self.add_link('guide', self.parent.page.page_id) - elif isinstance(self.node, ast.Interface): - self.page_type = 'guide' - self.page_style = 'interface' - self.add_link('guide', self.parent.page.page_id) - elif isinstance(self.node, ast.Function): - if self.node.is_constructor: - self.page_style = 'constructor' - self.add_link('guide', self.parent.page.page_id, 'constructor') - elif self.node.is_method: - self.page_style = 'method' - self.add_link('guide', self.parent.page.page_id, 'method') - else: - self.page_style = 'function' - self.add_link('guide', self.parent.page.page_id, 'function') - elif isinstance(self.node, ast.Property): - self.page_style = 'property' - self.add_link('guide', self.parent.page.page_id, 'property') - elif isinstance(self.node, ast.Signal): - self.page_style = 'signal' - self.add_link('guide', self.parent.page.page_id, 'signal') - - def render(self, writer): - with writer.tagcontext('page', [ - ('id', self.page_id), - ('type', self.page_type), - ('style', self.page_style), - ('xmlns', XMLNS), ('xmlns:ui', XMLNS_UI)]): - with writer.tagcontext('info'): - for linktype, xref, group in self.links: - if group is not None: - writer.write_tag('link', [ - ('type', linktype), ('xref', xref), ('group', group)]) - else: - writer.write_tag('link', [ - ('type', linktype), ('xref', xref)]) - writer.write_tag('title', [], self.title) - if isinstance(self.node, ast.Annotated): - self.render_doc(writer, self.node.doc) - if isinstance(self.node, ast.Class): - parent_chain = [] - node = self.node - while node.parent: - node = self.writer._transformer.lookup_giname(str(node.parent)) - parent_chain.append(node) - if node.namespace.name == 'GObject' and node.name == 'Object': - break - parent_chain.reverse() - def print_chain(chain): - with writer.tagcontext('item', []): - attrs = [] - title = self.writer._formatter.get_title(chain[0], None) - if hasattr(chain[0], 'page'): - attrs.append(('xref', chain[0].page.page_id)) - writer.write_tag('code', attrs, title) - if len(chain) > 1: - print_chain(chain[1:]) - with writer.tagcontext('synopsis', [('ui:expanded', 'no')]): - writer.write_tag('title', [], 'Hierarchy') - with writer.tagcontext('tree', []): - print_chain(parent_chain) - for linkstype, title in self.linksels: - with writer.tagcontext('links', [ - ('type', 'topic'), ('ui:expanded', 'yes'), - ('groups', linkstype)]): - writer.write_tag('title', [], title) - - def render_doc(self, writer, doc): - if doc is not None: - for para in doc.split('\n\n'): - writer.disable_whitespace() - with writer.tagcontext('p', []): - self.render_doc_inline(writer, para) - writer.enable_whitespace() - - def render_doc_inline(self, writer, text): - poss = [] - poss.append((text.find('#'), '#')) - poss = [pos for pos in poss if pos[0] >= 0] - poss.sort(cmp=lambda x, y: cmp(x[0], y[0])) - if len(poss) == 0: - writer.write_line(text, do_escape=True) - elif poss[0][1] == '#': - pos = poss[0][0] - writer.write_line(text[:pos], do_escape=True) - rest = text[pos + 1:] - link = re.split('[^a-zA-Z_:-]', rest, maxsplit=1)[0] - xref = self.writer._xrefs.get(link, link) - writer.write_tag('link', [('xref', xref)], link) - if len(link) < len(rest): - self.render_doc_inline(writer, rest[len(link):]) + def format(self, doc): + doc = MallardFormatter.format(self, doc) + doc = doc.replace('%NULL', 'None') + doc = doc.replace('%TRUE', 'True') + doc = doc.replace('%FALSE', 'False') + return doc class MallardWriter(object): - def __init__(self, formatter): - self._namespace = None - self._index = None - self._pages = [] - self._formatter = formatter - self._xrefs = {} - - def add_transformer(self, transformer): + def __init__(self, transformer, language): self._transformer = transformer - self._namespace = self._transformer._namespace - self._index = MallardPage(self, self._namespace, None) - - def write(self, output): - xmlwriter = XMLWriter() - self._index.render(xmlwriter) - fp = open(output, 'w') - fp.write(xmlwriter.get_xml()) - fp.close() + self._language = language - for page in self._pages: - xmlwriter = XMLWriter() - page.render(xmlwriter) - fp = open(os.path.join(os.path.dirname(output), page.page_id + '.page'), 'w') - fp.write(xmlwriter.get_xml()) - fp.close() - - def _render_page_object_hierarchy(self, page_node): - parent_chain = self._get_parent_chain(page_node) - parent_chain.append(page_node) - lines = [] + if self._language == 'C': + self._formatter = MallardFormatterC(self._transformer) + elif self._language == 'Python': + self._formatter = MallardFormatterPython(self._transformer) + else: + raise SystemExit("Unsupported language: %s" % language) - for level, parent in enumerate(parent_chain): - prepend = "" - if level > 0: - prepend = _space((level - 1)* 6) + " +----" - lines.append(_space(2) + prepend + self._formatter.get_class_name(parent)) + def write(self, output): + nodes = [self._transformer.namespace] + for node in self._transformer.namespace.itervalues(): + if isinstance(node, ast.Function) and node.moved_to is not None: + continue + if getattr(node, 'disguised', False): + continue + nodes.append(node) + if isinstance(node, (ast.Class, ast.Interface, ast.Record)): + nodes += getattr(node, 'methods', []) + nodes += getattr(node, 'constructors', []) + nodes += getattr(node, 'static_methods', []) + nodes += getattr(node, 'virtual_methods', []) + nodes += getattr(node, 'properties', []) + nodes += getattr(node, 'signals', []) + for node in nodes: + self._render_node(node, output) + + def _render_node(self, node, output): + namespace = self._transformer.namespace + if isinstance(node, ast.Namespace): + template_name = 'mallard-%s-namespace.tmpl' % self._language + page_id = 'index' + elif isinstance(node, (ast.Class, ast.Interface)): + template_name = 'mallard-%s-class.tmpl' % self._language + page_id = '%s.%s' % (namespace.name, node.name) + elif isinstance(node, ast.Record): + template_name = 'mallard-%s-record.tmpl' % self._language + page_id = '%s.%s' % (namespace.name, node.name) + elif isinstance(node, ast.Function): + template_name = 'mallard-%s-function.tmpl' % self._language + if node.parent is not None: + page_id = '%s.%s.%s' % (namespace.name, node.parent.name, node.name) + else: + page_id = '%s.%s' % (namespace.name, node.name) + elif isinstance(node, ast.Enum): + template_name = 'mallard-%s-enum.tmpl' % self._language + page_id = '%s.%s' % (namespace.name, node.name) + elif isinstance(node, ast.Property) and node.parent is not None: + template_name = 'mallard-%s-property.tmpl' % self._language + page_id = '%s.%s-%s' % (namespace.name, node.parent.name, node.name) + elif isinstance(node, ast.Signal) and node.parent is not None: + template_name = 'mallard-%s-signal.tmpl' % self._language + page_id = '%s.%s-%s' % (namespace.name, node.parent.name, node.name) + else: + template_name = 'mallard-%s-default.tmpl' % self._language + page_id = '%s.%s' % (namespace.name, node.name) - self._writer.disable_whitespace() - self._writer.write_line("\n".join(lines)) - self._writer.enable_whitespace() + if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ: + top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR'] + template_dir = os.path.join(top_srcdir, 'giscanner') + else: + template_dir = os.path.dirname(__file__) + + file_name = os.path.join(template_dir, template_name) + file_name = os.path.abspath(file_name) + template = Template(filename=file_name, output_encoding='utf-8', + module_directory=tempfile.gettempdir()) + result = template.render(namespace=namespace, + node=node, + page_id=page_id, + formatter=self._formatter) + + output_file_name = os.path.join(os.path.dirname(output), + page_id + '.page') + fp = open(output_file_name, 'w') + fp.write(result) + fp.close() |