[gobject-introspection/wip/transformer] Generate GTK DocBook Docs via GIRs
- From: Colin Walters <walters src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gobject-introspection/wip/transformer] Generate GTK DocBook Docs via GIRs
- Date: Wed, 28 Jul 2010 21:59:55 +0000 (UTC)
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(""")
+
+ 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(""")
+ 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]