[gobject-introspection/wip/transformer] Generate GTK DocBook Docs via GIRs



commit 27aeb761ec50c1c9c8fe56b92922c83882dca678
Author: Zachary Goldberg <zgoldberg src gnome org>
Date:   Wed Jul 28 17:45:48 2010 -0400

    Generate GTK DocBook Docs via GIRs
    
    Can generate with:
    tools/gidocgen ~/YOURLIB.gir /path/to/new/YOURLIB.sgml
    gtkdoc-mkhtml YOURLIB /path/to/new/YOURLIB.sgml
    gtkdoc-fixxref --htmldir=./ --module-dir=./

 .gitignore                    |    1 +
 TODO                          |    1 +
 giscanner/gicodeformatters.py |  205 +++++++++++++++++++++
 giscanner/gidocgen.py         |  402 +++++++++++++++++++++++++++++++++++++++++
 tools/Makefile.am             |   10 +-
 tools/g-ir-docgen.in          |   39 ++++
 6 files changed, 655 insertions(+), 3 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 561275e..2637dd0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,3 +120,4 @@ tests/extended.gir.test
 tools/g-ir-compiler
 tools/g-ir-generate
 tools/g-ir-scanner
+tools/g-ir-docgen
diff --git a/TODO b/TODO
old mode 100644
new mode 100755
index 600454a..5086633
--- a/TODO
+++ b/TODO
@@ -1,5 +1,6 @@
 GIR XML format
 ----------
+- Add "Since" keyword to GIR file
 - Document the format better
 - Add attributes to connect signals to their default handlers
   and wrappers to their vfuncs
diff --git a/giscanner/gicodeformatters.py b/giscanner/gicodeformatters.py
new file mode 100644
index 0000000..3f2b981
--- /dev/null
+++ b/giscanner/gicodeformatters.py
@@ -0,0 +1,205 @@
+from .ast import *
+
+METHOD = "method"
+CLASS  = "class"
+ENUM   = "enum"
+CONSTANT = "constant"
+CONSTRUCTOR = "constructor"
+FIELD = "field"
+PROPERTY = "property"
+
+ENTITY_TYPES = [
+    METHOD,
+    CLASS,
+    ENUM,
+    CONSTANT,
+    CONSTRUCTOR,
+    FIELD,
+    PROPERTY
+    ]
+
+def space(num):
+    return " " * num
+
+class DocBookFormatter(object):
+    @classmethod
+    def render(clazz, writer, entity, **args):
+        if entity.get_type() == METHOD:
+            clazz.render_method(writer, entity.get_data(), **args)
+        elif entity.get_type() == ENUM:
+            clazz.render_enum(writer, entity.get_data(), **args)
+        elif entity.get_type() == FIELD:
+            clazz.render_field(writer, entity.get_data(), **args)
+
+    @classmethod
+    def render_field(clazz, writer, enum, divs=True, **args):
+        writer.write_line("%s - %s" % (enum.name, clazz.get_type_string(enum.type)), do_escape=True)
+
+    @classmethod
+    def render_enum(clazz, writer, enum, divs=True, **args):
+        writer.write_line("%s - %s" % (enum.name, enum.symbol))
+
+    @classmethod
+    def render_typelinkname(clazz, type, namespace):
+        """
+        If the typename is in the current namespace then use the 
+        language naming conventions.  Otherwise just use the ctype
+        name.
+        """
+        link_dest = type.ctype.replace("*", "")
+        if type.target_giname:
+            ns = type.target_giname.split('.')
+            if ns[0] == namespace:
+                link_dest = "%s_%s" % (
+                    type.ctype.replace("*", ""),
+                    clazz.name)
+
+        return link_dest
+        
+
+    @classmethod
+    def render_method(clazz, writer, method, divs=True, method_name_link=False):
+        writer.disable_whitespace()
+
+        if divs:
+            writer.push_tag("programlisting")
+
+        link_dest = clazz.render_typelinkname(method.retval.type,
+                                          method.namespace.name)
+                    
+        writer.push_tag("link", [("linkend", link_dest)])
+        
+        writer.write_tag("returnvalue", [], clazz.get_type_string(method.retval.type))
+        writer.pop_tag()
+            
+        writer.write_line(space(20 - len(clazz.get_type_string(method.retval.type))))
+
+        if method_name_link:
+            writer.write_tag("link", [("linkend",
+                                       "%s-details_%s" % (method.name,
+                                                          clazz.name))],
+                             clazz.get_method_name(method)) 
+        else:
+            writer.write_line(clazz.get_method_name(method))
+            
+        writer.write_line("%s(" % space(40 - len(clazz.get_method_name(method))))
+
+        first_param = True
+        parameters = method.parameters
+        parameters = clazz.update_parameters(method, parameters)
+        for param in parameters:
+            if not first_param:
+                writer.write_line("\n%s" % space(61))
+            else:
+                first_param = False
+                
+            writer.push_tag("parameter", [])
+            writer.push_tag("link", [("linkend", "%s_%s" % (
+                            param.type.ctype.replace("*", ""),
+                            clazz.name))])
+            writer.write_tag("type", [], clazz.get_type_string(param.type))
+            writer.pop_tag()
+            if not param == parameters[-1]:
+                comma = ", "
+            else:
+                comma = ""
+
+            writer.write_line(" %s%s" % (param.argname, comma))
+            writer.pop_tag()
+            
+        writer.write_line(");\n")
+        
+        if divs:
+            writer.pop_tag()        
+            writer.write_tag("para", [], method.doc)
+            writer.push_tag("variablelist", [("role", "params")])
+
+            for param in parameters:
+
+                doc = "(%s) (transfer %s)" % (param.direction,
+                                              param.transfer)
+                if param.allow_none:
+                    doc += " (allow-none)"
+
+                if param.skip:
+                    doc += " (skip)"
+
+                writer.push_tag("varlistentry")
+                writer.push_tag("term")
+                writer.write_tag("parameter", [], "%s: %s:" % (param.argname,
+                                                               doc))
+                writer.pop_tag()
+
+                writer.push_tag("listitem")
+
+                writer.write_tag("simpara", [], param.doc or "")
+                writer.pop_tag()
+                writer.pop_tag()
+
+            writer.pop_tag()
+            
+        writer.enable_whitespace()
+
+class CDocBookFormatter(DocBookFormatter):
+    name = "C"
+    seperator = ""
+
+    def __init__(self):
+        pass
+
+    @classmethod
+    def get_type_string(clazz, type):
+        if not type or type.ctype == "None":
+            return "void"
+        
+        if type.ctype:
+            return str(type.ctype)
+        else:
+            return str(type)
+
+    @classmethod
+    def get_method_name(clazz, method):
+        return method.symbol
+
+    @classmethod
+    def render_parameter(clazz, param_type, param_name):
+        return "%s %s" % (param_type, param_name)
+    
+    @classmethod
+    def update_parameters(clazz, method, parameters):
+        if not method.is_method:
+            return parameters
+
+        param = Parameter("self", method.parent_class.glib_type_struct,
+                                    transfer=None)
+
+        parameters = [p for p in parameters] # Make a copy
+        if not param.type.ctype:
+            param.type.ctype = method.parent_class.type_name + "*"
+
+        parameters = [param] + parameters
+
+        return parameters
+
+class PythonDocBookFormatter(DocBookFormatter):
+    name = "Python"
+    seperator = "."
+
+    def __init__(self):
+        pass
+ 
+    @classmethod
+    def get_type_string(clazz, type):
+        return str(type)
+
+    @classmethod
+    def get_method_name(clazz, method):
+        return method.name
+
+    @classmethod
+    def render_parameter(clazz, param_type, param_name):
+        return "%s %s" % (param_type, param_name)
+
+    @classmethod
+    def update_parameters(clazz, method, parameters):
+        return parameters
diff --git a/giscanner/gidocgen.py b/giscanner/gidocgen.py
new file mode 100644
index 0000000..6526798
--- /dev/null
+++ b/giscanner/gidocgen.py
@@ -0,0 +1,402 @@
+import sys
+from .ast import *
+from .girparser import GIRParser
+from .xmlwriter import XMLWriter
+from .gicodeformatters import CDocBookFormatter, PythonDocBookFormatter, space
+import logging
+
+FORMATTERS = [CDocBookFormatter, PythonDocBookFormatter]
+
+class GIDocGenerator(object):
+
+    def __init__(self, girfile):
+        self.girfile = girfile
+        self.parse_gir()
+
+    def parse_gir(self):
+        logging.debug("Initializing Parser")
+        self.parser = GIRParser()
+        
+        logging.debug("Starting Parser")
+        self.parser.parse(self.girfile)
+        
+        logging.debug("Parsing Complete")
+        
+    def generate(self, writer):
+        ns = self.parser.get_namespace()
+        
+        writer.set_name(ns.name)
+        
+        writer.add_page(DocPage(ns, ns.name, "Details about namespace should go here which are not currently scanned by the gir"))
+        
+        for name, clazz in ns.iteritems():
+            page = writer.add_page(DocPage(ns, name))
+            
+            if "methods" in dir(clazz) and clazz.methods:
+                for const in clazz.constructors:
+                    const.parent_class = clazz
+                    const.namespace = ns
+                    page.add_entity(DocEntity(const.name,
+                                              "method",
+                                              const))
+                for method in clazz.methods:
+                    method.parent_class = clazz
+                    method.namespace = ns
+                    page.add_entity(DocEntity(method.name,
+                                              "method",
+                                              method))
+            if "members" in dir(clazz) and clazz.members:
+                for member in clazz.members:
+                    member.parent_class = clazz
+                    member.namespace = ns
+                    page.add_entity(DocEntity(member.name,
+                                              "enum",
+                                              member))
+            if "fields" in dir(clazz) and clazz.fields:
+                for field in clazz.fields:
+                    field.parent_class = clazz
+                    field.namespace = ns
+                    page.add_entity(DocEntity(field.name,
+                                              "field",
+                                              field))
+            if "properties" in dir(clazz) and clazz.properties:
+                for prop in clazz.properties:
+                    prop.parent_class = clazz
+                    prop.namespace = ns
+                    page.add_entity(DocEntity(prop.name,
+                                              "property",
+                                              prop))
+        writer.write()
+
+        
+class DocWriter(object):
+    def __init__(self):
+        self.pages = []
+        self.name = ""
+
+    def add_header_page(self, name, description):
+        pass
+
+    def set_name(self, name):
+        self.name = name
+
+    def add_page(self, page):
+        self.pages.append(page)
+        return self.pages[-1]
+
+class DocBookWriter(DocWriter):
+    def __init__(self, doc_location):
+        super(DocBookWriter, self).__init__()
+
+        self.doc_location = doc_location
+        self.writer = None
+
+    def write(self):
+        self.writer = XMLWriter()
+        self.writer.push_tag("book", [("xml:id", "page_%s" % self.name),
+                                     ("xmlns", "http://docbook.org/ns/docbook";),
+                                     ("version", "5.0")])
+
+        self.writer.write_tag("title",[], "%s Documentation" % self.name)
+        self.writer.write_tag("chapter", [], "Classes")
+
+        for page in self.pages:
+            for formatter in FORMATTERS:
+                self.writer.push_tag("refentry", [
+                        ("id", "%s-%s_%s" % (page.namespace.name,
+                                             page.name,
+                                             formatter.name))])
+                self.writer.push_tag("refmeta")
+                self.writer.write_tag("refentrytitle", [
+                        ("role", "top_of_page"),
+                        ("id", "%s-%s.top_of_page" % (page.namespace.name,
+                                                      page.name))],
+                                     "%s%s - %s" % (page.namespace.name,
+                                                    page.name, formatter.name))
+
+                self.writer.write_tag("manvolumenum", [], "1")
+                self.writer.write_tag("refmiscinfo", [], page.namespace.name)
+                self.writer.pop_tag()
+                self.writer.push_tag("refnamediv")
+                self.writer.write_tag("refname", [],
+                                      "%s%s%s" % (page.namespace.name,
+                                                     formatter.seperator,
+                                                     page.name))
+
+                self.writer.write_tag("refpurpose", [], page.get_doc())
+                self.writer.pop_tag()
+                            
+                self._render_page(page, formatter)
+                self.writer.pop_tag()
+
+        self.writer.pop_tag()
+
+        file = open(self.doc_location, 'w')
+        file.write(self.writer.get_xml())
+        file.close()
+
+    def _render_entity(self, entity, formatter):
+        
+        with self.writer.tagcontext('refsect1', 
+                                    [('id', "%s-details_%s" % (entity.get_name(),
+                                                               formatter.name)),
+                                     ("role", "details")]):
+            self.writer.write_tag("title", [("role", "details.title")],
+                                  "Details")
+            
+        self.writer.push_tag('refsect2', 
+                             [('id', "%s-function" % entity.get_name()),
+                              ('role', 'struct')])
+        self.writer.write_tag("title", [], entity.get_name())
+            
+        
+        with self.writer.tagcontext("indexterm",
+                                    [("zone", "%s" % entity.get_name())]):
+            self.writer.write_tag("primary", [], entity.get_name())
+                
+                
+        formatter.render(self.writer, entity)    
+
+        self.writer.pop_tag()
+                
+
+    def _render_page(self, page, formatter):
+        self.writer.write_tag("anchor", [("id",
+                                          "ch_%s_%s" % (page.name,
+                                                        formatter.name))])
+        self.writer.write_tag("anchor", [("id",
+                                          "%s%s_%s" % (
+                        page.namespace.name,
+                        page.name,
+                        formatter.name))])
+                                                      
+        with self.writer.tagcontext('para'):
+            self.writer.disable_whitespace()
+            self.writer.write_line("Other Languages: ")
+            for fmtr in FORMATTERS:
+                self.writer.write_tag("link", 
+                                      [("linkend", "ch_%s_%s" % (
+                                page.name,
+                                fmtr.name))], "%s " % fmtr.name)
+            self.writer.enable_whitespace()
+                                            
+        self.writer.write_tag("para", [], page.description)
+            
+        with self.writer.tagcontext("refsynopsisdiv", [
+                ('id', '%s.synopsis' % page.name),
+                ('role', 'synopsis')
+                ]):
+            self.writer.write_tag("title", [("role", "synopsis.title")],
+                                  "Synopsis")
+
+            with self.writer.tagcontext('synopsis'):
+                for entity in page.get_entities():
+                    formatter.render(self.writer, entity, 
+                                     divs=False, method_name_link=True)
+
+
+        classes = []        
+        if (page.get_entities() and 
+            "type_name" in 
+            dir(page.get_entities()[0].get_data().parent_class)):
+
+            bottom_class = page.get_entities()[0].get_data().parent_class
+            if bottom_class.type_name:
+                classes = [Type(bottom_class.type_name,
+                                bottom_class.type_name)]
+                try:
+                    cur = bottom_class.parent
+                except:
+                    cur = None
+
+                while cur:
+                    classes.append(cur)
+                    if "parent" in dir(cur):
+                        try:
+                            cur = cur.parent
+                        except:
+                            cur = None
+                    else:
+                        cur = None
+
+            classes.append(Type("GObject", "GObject"))
+            classes.reverse()
+
+        with self.writer.tagcontext("refsect1", [
+                ("id", "%s-%s_%s.object-hierarchy" % (page.namespace.name,
+                                                      page.name,
+                                                      formatter.name)),
+                ("role", "object_hieraarchy")]):
+            self.writer.write_tag("title", [
+                    ("role", "object_hierarchy.title")],
+                                  "Object Hierarchy")
+            self.writer.push_tag("synopsis")
+            indent = ""
+            arrow = ""
+            self.writer.disable_whitespace()
+            for clazz in classes:
+                self.writer.write_tag("link", [
+                        ("linkref", formatter.get_type_string(clazz))], 
+                                      "%s%s%s\n" % (
+                        indent,
+                        arrow,
+                        formatter.get_type_string(clazz)
+                        ))
+                if not indent:
+                    indent = " "
+                else:
+                    indent += "     "
+
+                if indent:
+                    arrow = "+----"
+            self.writer.enable_whitespace()
+            self.writer.pop_tag()
+            
+        with self.writer.tagcontext("refsect1", 
+                                    [
+                ("id", "%s-%s_%s.properties" % (
+                        page.namespace.name,
+                        page.name,
+                        formatter.name)),
+                ("role", "properties")
+                ]):
+            self.writer.write_tag("title", [("role", "properties.title")],
+                                  "Properties")
+
+            self.writer.push_tag("synopsis")
+            self.writer.disable_whitespace()
+            for prop in page.get_entities("property"):
+                self.writer.write_line("&quot;")
+
+                self.writer.write_tag("link", [
+                        ("linkend", "%s-%s-%s_%s" % (
+                                page.namespace.name,
+                                page.name,
+                                prop.get_data().name,
+                                formatter.name
+                                ))],
+                                      prop.get_data().name)
+                self.writer.write_line("&quot;")
+                self.writer.write_line(space(30 - len(prop.get_data().name)))
+                self.writer.write_tag("link", [
+                        ("linkend", 
+                         formatter.render_typelinkname(prop.get_data().type,
+                                                       page.namespace.name))],
+                        formatter.get_type_string(
+                            prop.get_data().type))
+                self.writer.write_line("%s: " % space(
+                        25 - len(formatter.get_type_string(
+                            prop.get_data().type))))
+                
+                rwstring = []
+                if prop.get_data().readable:
+                    rwstring.append("Read")
+                if prop.get_data().writable:
+                    rwstring.append("Write")
+                if prop.get_data().construct_only:
+                    rwstring.append("Construct Only")
+
+                self.writer.write_line(" / ".join(rwstring))
+                self.writer.write_line("\n")
+
+            self.writer.enable_whitespace()
+            self.writer.pop_tag()
+
+
+        with self.writer.tagcontext("refsect1", [
+                ("id", "%s-%s_%s.description" % (page.namespace.name,
+                                                 page.name,
+                                                 formatter.name)),
+                ("role", "desc")
+                ]):
+            self.writer.write_tag("title", [("role", "desc.title")],
+                                  "Description")
+
+            self.writer.write_tag("para", [], page.get_doc())
+            
+        for entity in page.get_entities():
+            self._render_entity(entity, formatter)
+    
+class DocEntity(object):
+    def __init__(self, entity_name, entity_type, entity_data):
+        self.entity_name = entity_name
+        self.entity_type = entity_type
+        self.entity_data = entity_data
+
+    def get_data(self):
+        return self.entity_data
+    
+    def get_type(self):
+        return self.entity_type
+
+    def get_name(self):
+        return self.entity_name
+        
+class DocPage(object):
+    def __init__(self, namespace, name, description=""):
+        self.entities = []
+        self.namespace = namespace
+        self.name = name
+        self.description = description
+
+    def add_entity(self, entity_data):
+        self.entities.append(entity_data)
+
+    def get_entities(self, type=None):
+        returnvalues = []
+        for entity in self.entities:
+            if not type or entity.get_type() == type:
+                returnvalues.append(entity)
+
+        return returnvalues
+                
+
+    def get_doc(self):
+        desc = ""
+        if self.get_entities():
+            desc = self.get_entities()[0].get_data().parent_class.doc
+        return desc
+
+class TextDocWriter(DocWriter):
+
+    def __init__(self):
+        self.lines = []
+        self.pages = []
+
+    def write(self):
+        self._render_pages()
+        print '\n'.join(self.lines)
+        
+    def _render_entity(self, entity):
+        if entity.get_type() == METHOD:
+            method = entity.get_data()
+                      
+
+            return CDocFormatter.render_method(
+                method.name,
+                ', '.join(map(lambda p: CDocFormatter.render_parameter(
+                            p.type.ctype,
+                            p.argname), 
+                         method.parameters)),
+                method.retval.type.ctype,
+                method.doc)
+                
+        else:
+            return entity
+
+    def _render_pages(self):
+        for page in self.pages:
+            self.lines.append("\n-- %s --\n%s" % (page.name,
+                                                  page.description))
+            for entity in page.get_entities():
+                self.lines.append("\t%s" % self._render_entity(
+                        entity))
+
+
+class MallardDocWriter(DocWriter):
+    def __init__(self, doc_location):
+        doc_location = doc_location
+
+    def write(self):
+        pass
+
diff --git a/tools/Makefile.am b/tools/Makefile.am
old mode 100644
new mode 100755
index 31acd02..77499a0
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -3,13 +3,17 @@ INCLUDES = \
 	-I$(top_srcdir)/girepository
 
 bin_PROGRAMS = g-ir-compiler g-ir-generate
-bin_SCRIPTS = g-ir-scanner
-EXTRA_DIST = g-ir-scanner.in
+bin_SCRIPTS = g-ir-scanner g-ir-docgen
+EXTRA_DIST = g-ir-scanner.in g-ir-docgen.in
 
 g-ir-scanner: g-ir-scanner.in Makefile
 	$(AM_V_GEN) sed -e s,@libdir\@,$(libdir), -e s,@PYTHON\@,$(PYTHON), $< > $  tmp && mv $  tmp $@
 	@chmod a+x $@
 
+g-ir-docgen: g-ir-docgen.in Makefile
+	$(AM_V_GEN) sed -e s,@libdir\@,$(libdir), -e s,@PYTHON\@,$(PYTHON), $< > $  tmp && mv $  tmp $@
+	@chmod a+x $@
+
 g_ir_compiler_SOURCES = compiler.c	
 g_ir_compiler_CFLAGS = $(GIREPO_CFLAGS)
 g_ir_compiler_LDADD = \
@@ -28,4 +32,4 @@ GCOVSOURCES =					\
 	$(g_ir_compiler_SOURCES)		\
 	$(g_ir_generate_SOURCES)
 
-CLEANFILES=g-ir-scanner
+CLEANFILES=g-ir-scanner g-ir-docgen
diff --git a/tools/g-ir-docgen.in b/tools/g-ir-docgen.in
new file mode 100755
index 0000000..9ee85bf
--- /dev/null
+++ b/tools/g-ir-docgen.in
@@ -0,0 +1,39 @@
+#! PYTHON@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2008  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 os
+import sys
+
+# This only works on unix systems
+currentdir = os.path.dirname(os.path.abspath(sys.argv[0]))
+current_name = os.path.basename(currentdir)
+if current_name == 'tools':
+    path = os.path.abspath(os.path.join(currentdir, '..'))
+else:
+    # This is a private directory, we don't want to pollute the global
+    # namespace.
+    path = os.path.join('@libdir@', 'gobject-introspection')
+sys.path.insert(0, path)
+
+from giscanner.gidocgen import *
+docgen = GIDocGenerator(sys.argv[1])
+docgen.generate(DocBookWriter(sys.argv[2]))
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]