[gobject-introspection] doctool: improve Gjs documentation



commit 75d25b7f47542aa003c92ce576b6e82bae66aec9
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Wed Feb 19 16:19:53 2014 +0100

    doctool: improve Gjs documentation
    
    - Add documentation for structures, fields, constants and
      callbacks
    - Improve the synopsis for interfaces to have prerequisites
      and known implementations
    - Respect gjs constraints for field writability
    - Format in and out parameters for callables according to GJS
      conventions
    - Format property names according to the GJS API
    - Show boxed constructors according to how they can be used
      in the gjs API
    
    https://bugzilla.gnome.org/show_bug.cgi?id=724735

 Makefile-giscanner.am                        |    9 +
 giscanner/doctemplates/C/callback.tmpl       |    4 +
 giscanner/doctemplates/C/field.tmpl          |    1 +
 giscanner/doctemplates/C/function.tmpl       |   17 +-
 giscanner/doctemplates/C/interface.tmpl      |    2 +
 giscanner/doctemplates/Gjs/callback.tmpl     |   27 ++
 giscanner/doctemplates/Gjs/class.tmpl        |   11 +-
 giscanner/doctemplates/Gjs/field.tmpl        |    9 +
 giscanner/doctemplates/Gjs/function.tmpl     |   23 +-
 giscanner/doctemplates/Gjs/interface.tmpl    |   17 ++
 giscanner/doctemplates/Gjs/property.tmpl     |    7 +-
 giscanner/doctemplates/Gjs/signal.tmpl       |   19 +-
 giscanner/doctemplates/Gjs/vfunc.tmpl        |   14 +-
 giscanner/doctemplates/Python/callback.tmpl  |   27 ++
 giscanner/doctemplates/Python/class.tmpl     |    9 +-
 giscanner/doctemplates/Python/field.tmpl     |    1 +
 giscanner/doctemplates/Python/function.tmpl  |   22 +-
 giscanner/doctemplates/Python/interface.tmpl |   16 ++
 giscanner/doctemplates/Python/property.tmpl  |    5 +-
 giscanner/doctemplates/Python/signal.tmpl    |   13 +-
 giscanner/doctemplates/Python/vfunc.tmpl     |   20 +-
 giscanner/doctemplates/base.tmpl             |   29 +--
 giscanner/doctemplates/class.tmpl            |   27 ++-
 giscanner/doctemplates/namespace.tmpl        |   11 +-
 giscanner/docwriter.py                       |  370 ++++++++++++++++++++++----
 25 files changed, 544 insertions(+), 166 deletions(-)
---
diff --git a/Makefile-giscanner.am b/Makefile-giscanner.am
index e3a934a..bccd815 100644
--- a/Makefile-giscanner.am
+++ b/Makefile-giscanner.am
@@ -62,33 +62,42 @@ nobase_dist_template_DATA =         \
        giscanner/doctemplates/base.tmpl        \
        giscanner/doctemplates/class.tmpl       \
        giscanner/doctemplates/namespace.tmpl   \
+       giscanner/doctemplates/C/callback.tmpl  \
        giscanner/doctemplates/C/class.tmpl     \
        giscanner/doctemplates/C/constructor.tmpl       \
        giscanner/doctemplates/C/default.tmpl   \
        giscanner/doctemplates/C/enum.tmpl      \
        giscanner/doctemplates/C/function.tmpl  \
+       giscanner/doctemplates/C/field.tmpl     \
+       giscanner/doctemplates/C/interface.tmpl \
        giscanner/doctemplates/C/method.tmpl            \
        giscanner/doctemplates/C/namespace.tmpl \
        giscanner/doctemplates/C/property.tmpl  \
        giscanner/doctemplates/C/record.tmpl    \
        giscanner/doctemplates/C/signal.tmpl    \
        giscanner/doctemplates/C/vfunc.tmpl     \
+       giscanner/doctemplates/Python/callback.tmpl     \
        giscanner/doctemplates/Python/class.tmpl        \
        giscanner/doctemplates/Python/constructor.tmpl  \
        giscanner/doctemplates/Python/default.tmpl      \
        giscanner/doctemplates/Python/enum.tmpl \
        giscanner/doctemplates/Python/function.tmpl     \
+       giscanner/doctemplates/Python/field.tmpl        \
+       giscanner/doctemplates/Python/interface.tmpl    \
        giscanner/doctemplates/Python/method.tmpl       \
        giscanner/doctemplates/Python/namespace.tmpl    \
        giscanner/doctemplates/Python/property.tmpl     \
        giscanner/doctemplates/Python/record.tmpl       \
        giscanner/doctemplates/Python/signal.tmpl       \
        giscanner/doctemplates/Python/vfunc.tmpl        \
+       giscanner/doctemplates/Gjs/callback.tmpl        \
        giscanner/doctemplates/Gjs/class.tmpl   \
        giscanner/doctemplates/Gjs/constructor.tmpl     \
        giscanner/doctemplates/Gjs/default.tmpl \
        giscanner/doctemplates/Gjs/enum.tmpl    \
        giscanner/doctemplates/Gjs/function.tmpl        \
+       giscanner/doctemplates/Gjs/field.tmpl   \
+       giscanner/doctemplates/Gjs/interface.tmpl       \
        giscanner/doctemplates/Gjs/method.tmpl  \
        giscanner/doctemplates/Gjs/namespace.tmpl       \
        giscanner/doctemplates/Gjs/property.tmpl        \
diff --git a/giscanner/doctemplates/C/callback.tmpl b/giscanner/doctemplates/C/callback.tmpl
new file mode 100644
index 0000000..f285ff6
--- /dev/null
+++ b/giscanner/doctemplates/C/callback.tmpl
@@ -0,0 +1,4 @@
+<%inherit file="./function.tmpl"/>
+<%block name="info">
+    ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+  </%block>
diff --git a/giscanner/doctemplates/C/field.tmpl b/giscanner/doctemplates/C/field.tmpl
new file mode 100644
index 0000000..b66ae92
--- /dev/null
+++ b/giscanner/doctemplates/C/field.tmpl
@@ -0,0 +1 @@
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/C/function.tmpl b/giscanner/doctemplates/C/function.tmpl
index 8d66943..3f9e627 100644
--- a/giscanner/doctemplates/C/function.tmpl
+++ b/giscanner/doctemplates/C/function.tmpl
@@ -6,7 +6,7 @@
         <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
       </api:returns>
       <api:name>${formatter.format_function_name(node)}</api:name>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 % if arg.type.ctype == '<varargs>':
       <api:varargs/>
 % else:
@@ -19,12 +19,12 @@
     </api:function>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-csrc">
+  <synopsis><code mime="text/x-csrc">
 ${node.retval.type.ctype} ${formatter.format_function_name(node)} (\
-% if not formatter.get_parameters(node):
+% if not formatter.get_in_parameters(node):
 void\
 % else:
-% for ix, arg in enumerate(formatter.get_parameters(node)):
+% for ix, arg in enumerate(formatter.get_in_parameters(node)):
 % if ix != 0:
 ${' ' * (len(formatter.format_type(node.retval.type)) + len(formatter.format_function_name(node)) + 3)}\
 % endif
@@ -33,18 +33,17 @@ ${' ' * (len(formatter.format_type(node.retval.type)) + len(formatter.format_fun
 % else:
 ${formatter.format_type(arg.type) | x} ${arg.argname}\
 % endif
-% if ix != len(formatter.get_parameters(node)) - 1:
+% if ix != len(formatter.get_in_parameters(node)) - 1:
 ,
 % endif
 % endfor
 % endif
 );
-</code></synopsis>
-</%block>
+  </code></synopsis></%block>
 <%block name="details">
-% if formatter.get_parameters(node) or node.retval:
+% if formatter.get_in_parameters(node) or node.retval:
 <terms>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 <item>
 <title><code>${arg.argname}</code></title>
 ${formatter.format(node, arg.doc)}
diff --git a/giscanner/doctemplates/C/interface.tmpl b/giscanner/doctemplates/C/interface.tmpl
new file mode 100644
index 0000000..3f18b02
--- /dev/null
+++ b/giscanner/doctemplates/C/interface.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/class.tmpl"/>
diff --git a/giscanner/doctemplates/Gjs/callback.tmpl b/giscanner/doctemplates/Gjs/callback.tmpl
new file mode 100644
index 0000000..d7b9779
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/callback.tmpl
@@ -0,0 +1,27 @@
+<%inherit file="/base.tmpl"/>
+<%block name="synopsis">
+  <synopsis><code mime="text/x-gjs">
+function on${node.name}(\
+${', '.join('%s: %s' % (arg.argname, formatter.format_type(arg.type, True)) for arg in 
formatter.get_in_parameters(node))}\
+): ${formatter.format_out_parameters(node)} {
+}
+  </code></synopsis></%block>
+<%block name="details">
+% if formatter.has_any_parameters(node):
+<terms>
+% for arg in formatter.get_in_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% for arg in formatter.get_out_parameters(node):
+<item>
+<title><code>${(arg.argname + ' (out)') if arg.direction == 'inout' else arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+</terms>
+% endif
+</%block>
+
diff --git a/giscanner/doctemplates/Gjs/class.tmpl b/giscanner/doctemplates/Gjs/class.tmpl
index 887c646..d843370 100644
--- a/giscanner/doctemplates/Gjs/class.tmpl
+++ b/giscanner/doctemplates/Gjs/class.tmpl
@@ -3,16 +3,19 @@
   <synopsis><code>
 const ${namespace.name} = imports.gi.${namespace.name};
 
-let ${formatter.to_underscores(node.name).lower()} = new ${namespace.name}.${node.name}(\
+let ${formatter.to_lower_camel_case(node.name)} = new ${namespace.name}.${node.name}(\
+% if isinstance(node, (ast.Class, ast.Interface)):
 % if len(node.properties) > 0:
 {
 % for ix, property_ in enumerate(node.properties):
-% if property_.construct or property_.construct_only or property_.writable:
+% if (property_.construct or property_.construct_only) and property_.writable:
     <link xref='${namespace.name}.${node.name}-${property_.name}'>${property_.name.replace('-', 
'_')}</link>: value,
 % endif
 % endfor
 }\
 % endif
+% else:
+${formatter.format_gboxed_constructor(node)}\
+% endif
 );
-  </code></synopsis>
-</%block>
+  </code></synopsis></%block>
diff --git a/giscanner/doctemplates/Gjs/field.tmpl b/giscanner/doctemplates/Gjs/field.tmpl
new file mode 100644
index 0000000..dda8246
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/field.tmpl
@@ -0,0 +1,9 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+    ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+    <title type="link" role="topic">${node.name}</title>
+</%block>
+<%block name="synopsis">
+  <synopsis><code mime="text/x-gjs">
+${node.parent.name}.${formatter.to_underscores(node)}: ${formatter.format_type(node.type, True)} 
(${formatter.format_property_flags(node)})
+  </code></synopsis></%block>
diff --git a/giscanner/doctemplates/Gjs/function.tmpl b/giscanner/doctemplates/Gjs/function.tmpl
index e0fd961..012978a 100644
--- a/giscanner/doctemplates/Gjs/function.tmpl
+++ b/giscanner/doctemplates/Gjs/function.tmpl
@@ -6,7 +6,7 @@
         <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
       </api:returns>
       <api:name>${node.symbol}</api:name>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 % if arg.type.ctype == '<varargs>':
       <api:varargs/>
 % else:
@@ -19,30 +19,29 @@
     </api:function>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-gjs">
+  <synopsis><code mime="text/x-gjs">
 function \
 ${node.name}(\
-${', '.join('%s:%s' % (arg.argname, formatter.format_type(arg.type)) for arg in 
formatter.get_parameters(node))}\
-):${formatter.format_type(node.retval.type)} {
+${', '.join('%s: %s' % (arg.argname, formatter.format_type(arg.type, True)) for arg in 
formatter.get_in_parameters(node))}\
+): ${formatter.format_out_parameters(node)} {
     // Gjs wrapper for ${node.symbol}()
 }
-</code></synopsis>
-</%block>
+  </code></synopsis></%block>
 <%block name="details">
-% if formatter.get_parameters(node) or node.retval:
+% if formatter.has_any_parameters(node):
 <terms>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 <item>
 <title><code>${arg.argname}</code></title>
 ${formatter.format(node, arg.doc)}
 </item>
 % endfor
-% if node.retval and node.retval.type.ctype != 'void':
+% for arg in formatter.get_out_parameters(node):
 <item>
-<title><code>Returns</code></title>
-${formatter.format(node, node.retval.doc)}
+<title><code>${(arg.argname + ' (out)') if arg.direction == 'inout' else arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
 </item>
-% endif
+% endfor
 </terms>
 % endif
 </%block>
diff --git a/giscanner/doctemplates/Gjs/interface.tmpl b/giscanner/doctemplates/Gjs/interface.tmpl
new file mode 100644
index 0000000..2f01f20
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/interface.tmpl
@@ -0,0 +1,17 @@
+<%inherit file="/class.tmpl"/>
+<%block name="synopsis">
+  <synopsis><code>
+const ${namespace.name} = imports.gi.${namespace.name};
+
+let ${formatter.to_underscores(node).lower()} = new ${namespace.name}.${node.name}(\
+% if len(node.properties) > 0:
+{
+% for ix, property_ in enumerate(node.properties):
+% if (property_.construct or property_.construct_only) and property_.writable:
+    <link xref='${namespace.name}.${node.name}-${property_.name}'>${property_.name.replace('-', 
'_')}</link>: value,
+% endif
+% endfor
+}\
+% endif
+);
+  </code></synopsis></%block>
diff --git a/giscanner/doctemplates/Gjs/property.tmpl b/giscanner/doctemplates/Gjs/property.tmpl
index 3316a00..dda8246 100644
--- a/giscanner/doctemplates/Gjs/property.tmpl
+++ b/giscanner/doctemplates/Gjs/property.tmpl
@@ -4,7 +4,6 @@
     <title type="link" role="topic">${node.name}</title>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-python">
-"${node.name}"             ${formatter.format_type(node.type)}                : 
${formatter.format_property_flags(node)}
-</code></synopsis>
-</%block>
+  <synopsis><code mime="text/x-gjs">
+${node.parent.name}.${formatter.to_underscores(node)}: ${formatter.format_type(node.type, True)} 
(${formatter.format_property_flags(node)})
+  </code></synopsis></%block>
diff --git a/giscanner/doctemplates/Gjs/signal.tmpl b/giscanner/doctemplates/Gjs/signal.tmpl
index 084d974..7c50816 100644
--- a/giscanner/doctemplates/Gjs/signal.tmpl
+++ b/giscanner/doctemplates/Gjs/signal.tmpl
@@ -4,21 +4,22 @@
     <title type="link" role="topic">${node.name}</title>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-python">
-function callback(${formatter.to_underscores(node.parent.name).lower()}, \
-% for arg in formatter.get_parameters(node):
-${arg.argname}:${formatter.format_type(arg.type)}, \
+  <synopsis><code mime="text/x-gjs">
+connect('${node.name}', function (${formatter.to_lower_camel_case(node.parent.name)}, \
+% for arg in formatter.get_in_parameters(node):
+% if arg.type.target_fundamental != 'none':
+${arg.argname}: ${formatter.format_type(arg.type, True)}, \
+% endif
 % endfor
-):${formatter.format_type(node.retval.type)};
-</code></synopsis>
-</%block>
+): ${formatter.format_type(node.retval.type, True)});
+  </code></synopsis></%block>
 <%block name="details">
 <terms>
 <item>
-<title><code>${formatter.to_underscores(node.parent.name).lower()}</code></title>
+<title><code>${formatter.to_lower_camel_case(node.parent.name)}</code></title>
 <p>instance of ${formatter.format_xref(node.parent)} that is emitting the signal</p>
 </item>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 <item>
 <title><code>${arg.argname}</code></title>
 ${formatter.format(node, arg.doc)}
diff --git a/giscanner/doctemplates/Gjs/vfunc.tmpl b/giscanner/doctemplates/Gjs/vfunc.tmpl
index 1cbe511..2bd1127 100644
--- a/giscanner/doctemplates/Gjs/vfunc.tmpl
+++ b/giscanner/doctemplates/Gjs/vfunc.tmpl
@@ -1,16 +1,15 @@
 <%inherit file="/base.tmpl"/>
 <%block name="synopsis">
-<synopsis><code mime="text/x-gjs">
+  <synopsis><code mime="text/x-gjs">
 function vfunc_${node.name}(\
-${', '.join('%s:%s' % (arg.argname, formatter.format_type(arg.type)) for arg in 
formatter.get_parameters(node))}\
-):${formatter.format_type(node.retval.type)} {
+${', '.join('%s: %s' % (arg.argname, formatter.format_type(arg.type, True)) for arg in 
formatter.get_in_parameters(node))}\
+): ${formatter.format_out_parameters(node)} {
 }
-</code></synopsis>
-</%block>
+  </code></synopsis></%block>
 <%block name="details">
-% if formatter.get_parameters(node) or node.retval:
+% if formatter.get_in_parameters(node) or node.retval:
 <terms>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 <item>
 <title><code>${arg.argname}</code></title>
 ${formatter.format(node, arg.doc)}
@@ -25,3 +24,4 @@ ${formatter.format(node, node.retval.doc)}
 </terms>
 % endif
 </%block>
+
diff --git a/giscanner/doctemplates/Python/callback.tmpl b/giscanner/doctemplates/Python/callback.tmpl
new file mode 100644
index 0000000..4fcbfe3
--- /dev/null
+++ b/giscanner/doctemplates/Python/callback.tmpl
@@ -0,0 +1,27 @@
+<%inherit file="/base.tmpl"/>
+<%block name="synopsis">
+  <synopsis><code mime="text/x-python">
+% if formatter.get_in_parameters(node):
+ accepts(${', '.join((formatter.format_type(arg.type) for arg in formatter.get_in_parameters(node)))})
+% endif
+ returns(${formatter.format_type(node.retval.type) | x})
+def on_${node.name}(${', '.join((arg.argname for arg in formatter.get_in_parameters(node)))}):
+  </code></synopsis></%block>
+<%block name="details">
+% if formatter.get_in_parameters(node) or node.retval:
+<terms>
+% for arg in formatter.get_in_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval and node.retval.type.ctype != 'void':
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/Python/class.tmpl b/giscanner/doctemplates/Python/class.tmpl
index 435b31a..51d25a9 100644
--- a/giscanner/doctemplates/Python/class.tmpl
+++ b/giscanner/doctemplates/Python/class.tmpl
@@ -3,7 +3,8 @@
   <synopsis><code>
 from gi.repository import ${namespace.name}
 
-${formatter.to_underscores(node.name).lower()} = ${namespace.name}.${node.name}(\
+${formatter.to_underscores(node).lower()} = ${namespace.name}.${node.name}(\
+% if isinstance(node, (ast.Class, ast.Interface)):
 % for ix, property_ in enumerate(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\
@@ -12,6 +13,6 @@ ${formatter.to_underscores(node.name).lower()} = ${namespace.name}.${node.name}(
 % endif
 % endif
 % endfor
-)\
-  </code></synopsis>
-</%block>
+% endif
+)
+  </code></synopsis></%block>
diff --git a/giscanner/doctemplates/Python/field.tmpl b/giscanner/doctemplates/Python/field.tmpl
new file mode 100644
index 0000000..b66ae92
--- /dev/null
+++ b/giscanner/doctemplates/Python/field.tmpl
@@ -0,0 +1 @@
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/Python/function.tmpl b/giscanner/doctemplates/Python/function.tmpl
index 072a118..7ad1ac0 100644
--- a/giscanner/doctemplates/Python/function.tmpl
+++ b/giscanner/doctemplates/Python/function.tmpl
@@ -6,7 +6,7 @@
         <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
       </api:returns>
       <api:name>${node.symbol}</api:name>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 % if arg.type.ctype == '<varargs>':
       <api:varargs/>
 % else:
@@ -19,24 +19,18 @@
     </api:function>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-python">
-% if formatter.get_parameters(node):
- accepts(\
-${', '.join((formatter.format_type(arg.type) for arg in formatter.get_parameters(node)))}\
-)
+  <synopsis><code mime="text/x-python">
+% if formatter.get_in_parameters(node):
+ accepts(${', '.join((formatter.format_type(arg.type) for arg in formatter.get_in_parameters(node)))})
 % endif
 @returns(${formatter.format_type(node.retval.type) | x})
-def \
-${node.name}(\
-${', '.join((formatter.format_parameter_name(node, arg) for arg in formatter.get_parameters(node)))}\
-):
+def ${node.name}(${', '.join((formatter.format_parameter_name(node, arg) for arg in 
formatter.get_in_parameters(node)))}):
     # Python wrapper for ${node.symbol}()
-</code></synopsis>
-</%block>
+  </code></synopsis></%block>
 <%block name="details">
-% if formatter.get_parameters(node) or node.retval:
+% if formatter.get_in_parameters(node) or node.retval:
 <terms>
-% for ix, arg in enumerate(formatter.get_parameters(node)):
+% for ix, arg in enumerate(formatter.get_in_parameters(node)):
 <item>
 <title><code>${formatter.format_parameter_name(node, arg)}</code></title>
 ${formatter.format(node, arg.doc)}
diff --git a/giscanner/doctemplates/Python/interface.tmpl b/giscanner/doctemplates/Python/interface.tmpl
new file mode 100644
index 0000000..b3596a8
--- /dev/null
+++ b/giscanner/doctemplates/Python/interface.tmpl
@@ -0,0 +1,16 @@
+<%inherit file="/class.tmpl"/>
+<%block name="synopsis">
+  <synopsis><code>
+from gi.repository import ${namespace.name}
+
+${formatter.to_underscores(node).lower()} = ${namespace.name}.${node.name}(\
+% for ix, property_ in enumerate(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></%block>
diff --git a/giscanner/doctemplates/Python/property.tmpl b/giscanner/doctemplates/Python/property.tmpl
index 3316a00..c93d59b 100644
--- a/giscanner/doctemplates/Python/property.tmpl
+++ b/giscanner/doctemplates/Python/property.tmpl
@@ -4,7 +4,6 @@
     <title type="link" role="topic">${node.name}</title>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-python">
+  <synopsis><code mime="text/x-python">
 "${node.name}"             ${formatter.format_type(node.type)}                : 
${formatter.format_property_flags(node)}
-</code></synopsis>
-</%block>
+  </code></synopsis></%block>
diff --git a/giscanner/doctemplates/Python/signal.tmpl b/giscanner/doctemplates/Python/signal.tmpl
index dc93110..c61b722 100644
--- a/giscanner/doctemplates/Python/signal.tmpl
+++ b/giscanner/doctemplates/Python/signal.tmpl
@@ -4,21 +4,20 @@
     <title type="link" role="topic">${node.name}</title>
 </%block>
 <%block name="synopsis">
-<synopsis><code mime="text/x-python">
-def callback(${formatter.to_underscores(node.parent.name).lower()}, \
-% for arg in formatter.get_parameters(node):
+  <synopsis><code mime="text/x-python">
+def callback(${formatter.to_underscores(node.parent).lower()}, \
+% for arg in formatter.get_in_parameters(node):
 ${arg.argname}, \
 % endfor
 user_param1, ...)
-</code></synopsis>
-</%block>
+  </code></synopsis></%block>
 <%block name="details">
 <terms>
 <item>
-<title><code>${formatter.to_underscores(node.parent.name).lower()}</code></title>
+<title><code>${formatter.to_underscores(node.parent).lower()}</code></title>
 <p>instance of ${formatter.format_xref(node.parent)} that is emitting the signal</p>
 </item>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 <item>
 <title><code>${arg.argname}</code></title>
 ${formatter.format(node, arg.doc)}
diff --git a/giscanner/doctemplates/Python/vfunc.tmpl b/giscanner/doctemplates/Python/vfunc.tmpl
index 98a3093..2c532e7 100644
--- a/giscanner/doctemplates/Python/vfunc.tmpl
+++ b/giscanner/doctemplates/Python/vfunc.tmpl
@@ -1,22 +1,16 @@
 <%inherit file="/base.tmpl"/>
 <%block name="synopsis">
-<synopsis><code mime="text/x-python">
-% if formatter.get_parameters(node):
- accepts(\
-${', '.join((formatter.format_type(arg.type) for arg in formatter.get_parameters(node)))}\
-)
+  <synopsis><code mime="text/x-python">
+% if formatter.get_in_parameters(node):
+ accepts(${', '.join((formatter.format_type(arg.type) for arg in formatter.get_in_parameters(node)))})
 % endif
 @returns(${formatter.format_type(node.retval.type) | x})
-def \
-do_${node.name}(\
-${', '.join((arg.argname for arg in formatter.get_parameters(node)))}\
-):
-</code></synopsis>
-</%block>
+def do_${node.name}(${', '.join((arg.argname for arg in formatter.get_in_parameters(node)))}):
+  </code></synopsis></%block>
 <%block name="details">
-% if formatter.get_parameters(node) or node.retval:
+% if formatter.get_in_parameters(node) or node.retval:
 <terms>
-% for arg in formatter.get_parameters(node):
+% for arg in formatter.get_in_parameters(node):
 <item>
 <title><code>${arg.argname}</code></title>
 ${formatter.format(node, arg.doc)}
diff --git a/giscanner/doctemplates/base.tmpl b/giscanner/doctemplates/base.tmpl
index 7898077..7a1d8db 100644
--- a/giscanner/doctemplates/base.tmpl
+++ b/giscanner/doctemplates/base.tmpl
@@ -6,24 +6,15 @@
       xmlns="http://projectmallard.org/1.0/";
       xmlns:api="http://projectmallard.org/experimental/api/";
       xmlns:ui="http://projectmallard.org/1.0/ui/";>
-  <info>
-    <%block name="info">
-      ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
-    </%block>
+  <info><%block name="info">
+    ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+</%block>\
   </info>
-  <title><%block name="title">${formatter.format_page_name(node)}</%block></title>
-  <%block name="synopsis">
-  </%block>
-  <%block name="doc">
-    ${formatter.format(node, node.doc)}
-  </%block>
-  <%block name="since_version">
-    % if node.version:
-    <p>Since ${node.version}</p>
-    % endif
-  </%block>
-  <%block name="details">
-  </%block>
-  <%block name="links">
-  </%block>
+  <title><%block name="title">${formatter.format_page_name(node)}</%block></title><%block 
name="synopsis"></%block><%block name="doc">
+${formatter.format(node, node.doc)}
+</%block><%block name="since_version">\
+% if node.version:
+  <p>Since ${node.version}</p>\
+% endif
+</%block><%block name="details"></%block><%block name="links"></%block>
 </page>
diff --git a/giscanner/doctemplates/class.tmpl b/giscanner/doctemplates/class.tmpl
index 7f8b686..8633386 100644
--- a/giscanner/doctemplates/class.tmpl
+++ b/giscanner/doctemplates/class.tmpl
@@ -1,6 +1,7 @@
 <%! page_type="guide" %>\
 <%inherit file="/base.tmpl"/>
 <%block name="details">
+% if isinstance(node, ast.Class):
   <synopsis>
     <title>Hierarchy</title>
     <tree>
@@ -13,8 +14,23 @@
 % endfor
     </tree>
   </synopsis>
+% elif isinstance(node, ast.Interface):
+  <synopsis>
+    <title>Prerequisites</title>
+    <p>${node.name} requires ${formatter.format_prerequisites(node)}</p>
+  </synopsis>
+  <synopsis>
+    <title>Known Implementations</title>
+    <p>${formatter.format_known_implementations(node)}</p>
+  </synopsis>
+% endif
 </%block>
-<%block name="links">
+<%block name="links">\
+  <links type="topic" ui:expanded="true"
+         api:type="function" api:mime="${formatter.mime_type}"
+         groups="constructor" style="linklist">
+    <title>Constructors</title>
+  </links>
   <links type="topic" ui:expanded="true"
          api:type="function" api:mime="${formatter.mime_type}"
          groups="method" style="linklist">
@@ -23,8 +39,9 @@
   <links type="topic" ui:expanded="true"
          api:type="function" api:mime="${formatter.mime_type}"
          groups="function" style="linklist">
-    <title>Functions</title>
+    <title>Static Functions</title>
   </links>
+% if isinstance(node, (ast.Class, ast.Interface)):
   <links type="topic" ui:expanded="true" groups="property" style="linklist">
     <title>Properties</title>
   </links>
@@ -34,7 +51,11 @@
   <links type="topic" ui:expanded="true" groups="vfunc" style="linklist">
     <title>Virtual functions</title>
   </links>
+% endif
+  <links type="topic" ui:expanded="true" groups="field" style="linklist">
+    <title>Fields</title>
+  </links>
   <links type="topic" ui:expanded="true" groups="#first #default #last" style="linklist">
     <title>Other</title>
-  </links>
+  </links>\
 </%block>
diff --git a/giscanner/doctemplates/namespace.tmpl b/giscanner/doctemplates/namespace.tmpl
index bb58bb1..d4edcec 100644
--- a/giscanner/doctemplates/namespace.tmpl
+++ b/giscanner/doctemplates/namespace.tmpl
@@ -1,11 +1,9 @@
 <%! page_type="guide" %>\
 <%inherit file="/base.tmpl"/>
-<%block name="doc">
-</%block>
-<%block name="info">
-</%block>
+<%block name="doc"></%block>
+<%block name="info"></%block>
 <%block name="links">
-  <links type="topic" ui:expanded="true" groups="class" style="linklist">
+  <links type="topic" ui:expanded="true" groups="class interface" style="linklist">
     <title>Classes</title>
   </links>
   <links type="topic" ui:expanded="true" groups="function" style="linklist">
@@ -15,5 +13,4 @@
     <title>Other</title>
   </links>
 </%block>
-<%block name="since_version">
-</%block>
+<%block name="since_version"></%block>
diff --git a/giscanner/docwriter.py b/giscanner/docwriter.py
index a4c817e..75a2b03 100644
--- a/giscanner/docwriter.py
+++ b/giscanner/docwriter.py
@@ -47,7 +47,7 @@ def make_page_id(node, recursive=False):
     if parent is None:
         return '%s.%s' % (node.namespace.name, node.name)
 
-    if isinstance(node, (ast.Property, ast.Signal, ast.VFunction)):
+    if isinstance(node, (ast.Property, ast.Signal, ast.VFunction, ast.Field)):
         return '%s-%s' % (make_page_id(parent, recursive=True), node.name)
     else:
         return '%s.%s' % (make_page_id(parent, recursive=True), node.name)
@@ -56,8 +56,10 @@ def make_page_id(node, recursive=False):
 def get_node_kind(node):
     if isinstance(node, ast.Namespace):
         node_kind = 'namespace'
-    elif isinstance(node, (ast.Class, ast.Interface)):
+    elif isinstance(node, (ast.Class, ast.Boxed, ast.Compound)):
         node_kind = 'class'
+    elif isinstance(node, ast.Interface):
+        node_kind = 'interface'
     elif isinstance(node, ast.Record):
         node_kind = 'record'
     elif isinstance(node, ast.Function):
@@ -75,6 +77,10 @@ def get_node_kind(node):
         node_kind = 'signal'
     elif isinstance(node, ast.VFunction) and node.parent is not None:
         node_kind = 'vfunc'
+    elif isinstance(node, ast.Callable):
+        node_kind = 'callback'
+    elif isinstance(node, ast.Field):
+        node_kind = 'field'
     else:
         node_kind = 'default'
 
@@ -170,9 +176,6 @@ class DocFormatter(object):
         return saxutils.escape(text)
 
     def should_render_node(self, node):
-        if isinstance(node, ast.Constant):
-            return False
-
         if getattr(node, "private", False):
             return False
 
@@ -184,7 +187,7 @@ class DocFormatter(object):
 
         result = ''
         for para in doc.split('\n\n'):
-            result += '<p>'
+            result += '  <p>'
             result += self.format_inline(node, para)
             result += '</p>'
         return result
@@ -295,7 +298,7 @@ class DocFormatter(object):
 
         return dispatch[kind](node, match, props)
 
-    def get_parameters(self, node):
+    def get_in_parameters(self, node):
         raise NotImplementedError
 
     def format_inline(self, node, para):
@@ -312,12 +315,12 @@ class DocFormatter(object):
     def format_function_name(self, func):
         raise NotImplementedError
 
-    def format_type(self, type_):
+    def format_type(self, type_, link=False):
         raise NotImplementedError
 
     def format_page_name(self, node):
         if isinstance(node, ast.Namespace):
-            return 'Index'
+            return node.name
         elif isinstance(node, ast.Function):
             return self.format_function_name(node)
         elif isinstance(node, ast.Property) and node.parent is not None:
@@ -326,6 +329,8 @@ class DocFormatter(object):
             return '%s::%s' % (self.format_page_name(node.parent), node.name)
         elif isinstance(node, ast.VFunction) and node.parent is not None:
             return '%s::%s' % (self.format_page_name(node.parent), node.name)
+        elif isinstance(node, ast.Field) and node.parent is not None:
+            return '%s->%s' % (self.format_page_name(node.parent), node.name)
         else:
             return make_page_id(node)
 
@@ -340,25 +345,46 @@ class DocFormatter(object):
             attrs = [('xref', make_page_id(node))] + attrdict.items()
             return xmlwriter.build_xml_tag('link', attrs)
 
+    def field_is_writable(self, field):
+        return True
+
     def format_property_flags(self, property_, construct_only=False):
         flags = []
+
         if property_.readable and not construct_only:
             flags.append("Read")
-        if property_.writable and not construct_only:
+        if property_.writable and not construct_only and \
+           self.field_is_writable(property_):
             flags.append("Write")
-        if property_.construct:
-            flags.append("Construct")
-        if property_.construct_only:
-            flags.append("Construct Only")
+        if isinstance(property_, ast.Property):
+            if property_.construct:
+                flags.append("Construct")
+            if property_.construct_only:
+                flags.append("Construct Only")
 
         return " / ".join(flags)
 
-    def to_underscores(self, string):
-        return to_underscores(string)
+    def to_underscores(self, node):
+        if isinstance(node, ast.Property):
+            return node.name.replace('-', '_')
+        elif node.name:
+            return to_underscores(node.name)
+        elif isinstance(node, ast.Callback):
+            return 'callback'
+        elif isinstance(node, ast.Union):
+            return 'anonymous_union'
+        elif isinstance(node, ast.Field):
+            return 'anonymous field'
+        else:
+            raise Exception('invalid node')
+
+    def to_lower_camel_case(self, string):
+        return string[0].lower() + string[1:]
 
     def get_class_hierarchy(self, node):
-        parent_chain = [node]
+        assert isinstance(node, ast.Class)
 
+        parent_chain = [node]
         while node.parent_type:
             node = self._transformer.lookup_typenode(node.parent_type)
             parent_chain.append(node)
@@ -366,6 +392,42 @@ class DocFormatter(object):
         parent_chain.reverse()
         return parent_chain
 
+    def format_prerequisites(self, node):
+        assert isinstance(node, ast.Interface)
+
+        if len(node.prerequisites) > 0:
+            if len(node.prerequisites) > 1:
+                return ', '.join(node.prerequisites[:-1]) + \
+                    ' and ' + node.prerequisites[-1]
+            else:
+                return node.prerequisites[0]
+        else:
+            return 'GObject.Object'
+
+    def format_known_implementations(self, node):
+        assert isinstance(node, ast.Interface)
+
+        node_name = node.namespace.name + '.' + node.name
+        impl = []
+
+        for c in node.namespace.itervalues():
+            if not isinstance(c, ast.Class):
+                continue
+            for implemented in c.interfaces:
+                if implemented.target_giname == node_name:
+                    impl.append(c)
+                    break
+
+        if len(impl) == 0:
+            return 'None'
+        else:
+            out = '%s is implemented by ' % (node.name,)
+            if len(impl) == 1:
+                return out + impl[0].name
+            else:
+                return out + ', '.join(i.name for i in impl[:-1]) + \
+                    ' and ' + impl[-1].name
+
 
 class DocFormatterC(DocFormatter):
     language = "C"
@@ -377,7 +439,7 @@ class DocFormatterC(DocFormatter):
         "NULL": "NULL",
     }
 
-    def format_type(self, type_):
+    def format_type(self, type_, link=False):
         if isinstance(type_, ast.Array):
             return self.format_type(type_.element_type) + '*'
         elif type_.ctype is not None:
@@ -394,7 +456,7 @@ class DocFormatterC(DocFormatter):
         else:
             return func.name
 
-    def get_parameters(self, node):
+    def get_in_parameters(self, node):
         return node.all_parameters
 
 
@@ -466,7 +528,7 @@ class DocFormatterPython(DocFormatterIntrospectableBase):
 
         return fundamental_types.get(name, name)
 
-    def format_type(self, type_):
+    def format_type(self, type_, link=False):
         if isinstance(type_, (ast.List, ast.Array)):
             return '[' + self.format_type(type_.element_type) + ']'
         elif isinstance(type_, ast.Map):
@@ -483,7 +545,7 @@ class DocFormatterPython(DocFormatterIntrospectableBase):
         else:
             return func.name
 
-    def get_parameters(self, node):
+    def get_in_parameters(self, node):
         return node.all_parameters
 
 
@@ -506,61 +568,155 @@ class DocFormatterGjs(DocFormatterIntrospectableBase):
 
         return False
 
+    def resolve_gboxed_constructor(self, node):
+        zero_args_constructor = None
+        default_constructor = None
+
+        for c in node.constructors:
+            if zero_args_constructor is None and \
+               len(c.parameters) == 0:
+                zero_args_constructor = c
+            if default_constructor is None and \
+               c.name == 'new':
+                default_constructor = c
+        if default_constructor is None:
+            default_constructor = zero_args_constructor
+        if default_constructor is None and \
+           len(node.constructors) > 0:
+            default_constructor = node.constructors[0]
+
+        node.gjs_default_constructor = default_constructor
+        node.gjs_zero_args_constructor = zero_args_constructor
+
+    def should_render_node(self, node):
+        if isinstance(node, (ast.Compound, ast.Boxed)):
+            self.resolve_gboxed_constructor(node)
+
+        if isinstance(node, ast.ErrorQuarkFunction):
+            return False
+        if isinstance(node, ast.Field):
+            if node.type is None:
+                return False
+            if isinstance(node.parent, ast.Class):
+                return False
+        if isinstance(node, ast.Union) and node.name is None:
+            return False
+        if isinstance(node, ast.Class):
+            is_gobject = False
+            parent = node
+            while parent:
+                if parent.namespace.name == 'GObject' and \
+                   parent.name == 'Object':
+                    is_gobject = True
+                    break
+                if not parent.parent_type:
+                    break
+                parent = self._transformer.lookup_typenode(parent.parent_type)
+            is_gparam = node.namespace.name == 'GObject' and \
+                node.name == 'ParamSpec'
+            if not is_gobject and not is_gparam:
+                return False
+        if isinstance(node, ast.Function) and node.is_constructor:
+            parent = node.parent
+            if isinstance(parent, (ast.Compound, ast.Boxed)):
+                if node == parent.gjs_default_constructor:
+                    return False
+            if isinstance(parent, ast.Class):
+                return False
+
+        return super(DocFormatterGjs, self).should_render_node(node)
+
     def format_fundamental_type(self, name):
         fundamental_types = {
+            "none": "void",
+            "gpointer": "void",
+            "gboolean": "Boolean",
+            "gint8": "Number(gint8)",
+            "guint8": "Number(guint8)",
+            "gint16": "Number(gint16)",
+            "guint16": "Number(guint16)",
+            "gint32": "Number(gint32)",
+            "guint32": "Number(guint32)",
+            "gchar": "Number(gchar)",
+            "guchar": "Number(guchar)",
+            "gshort": "Number(gshort)",
+            "gint": "Number(gint)",
+            "guint": "Number(guint)",
+            "gfloat": "Number(gfloat)",
+            "gdouble": "Number(gdouble)",
             "utf8": "String",
             "gunichar": "String",
-            "gchar": "String",
-            "guchar": "String",
-            "gboolean": "Boolean",
-            "gint": "Number",
-            "guint": "Number",
-            "glong": "Number",
-            "gulong": "Number",
-            "gint64": "Number",
-            "guint64": "Number",
-            "gfloat": "Number",
-            "gdouble": "Number",
-            "gchararray": "String",
-            "GParam": "GLib.Param",
-            "PyObject": "Object",
-            "GStrv": "[String]",
-            "GVariant": "GLib.Variant"}
+            "filename": "String",
+            "GType": "GObject.Type",
+            "GVariant": "GLib.Variant",
+            # These cannot be fully represented in gjs
+            "gsize": "Number(gsize)",
+            "gssize": "Number(gssize)",
+            "gintptr": "Number(gintptr)",
+            "guintptr": "Number(guintptr)",
+            "glong": "Number(glong)",
+            "gulong": "Number(gulong)",
+            "gint64": "Number(gint64)",
+            "guint64": "Number(guint64)",
+            "long double": "Number(long double)",
+            "long long": "Number(long long)",
+            "unsigned long long": "Number(unsigned long long)"}
 
         return fundamental_types.get(name, name)
 
-    def format_type(self, type_):
+    def format_type(self, type_, link=False):
         if isinstance(type_, (ast.List, ast.Array)):
-            return '[' + self.format_type(type_.element_type) + ']'
+            return 'Array(' + self.format_type(type_.element_type, link) + ')'
         elif isinstance(type_, ast.Map):
-            return '{%s: %s}' % (self.format_type(type_.key_type),
-                                 self.format_type(type_.value_type))
-        elif type_.target_fundamental == "none":
+            return '{%s: %s}' % (self.format_type(type_.key_type, link),
+                                 self.format_type(type_.value_type, link))
+        elif not type_ or type_.target_fundamental == "none":
             return "void"
         elif type_.target_giname is not None:
-            return type_.target_giname
+            giname = type_.target_giname
+            if link:
+                nsname = self._transformer.namespace.name
+                if giname.startswith(nsname + '.'):
+                    return '<link xref="%s">%s</link>' % (giname, giname)
+                else:
+                    resolved = self._transformer.lookup_typenode(type_)
+                    if resolved:
+                        ns = resolved.namespace
+                        return '<link href="../%s-%s/%s.page">%s</link>' % \
+                            (ns.name, str(ns.version), giname, giname)
+            return giname
         else:
             return self.format_fundamental_type(type_.target_fundamental)
 
     def format_function_name(self, func):
         if func.is_method:
             return "%s.prototype.%s" % (self.format_page_name(func.parent), func.name)
-        elif func.is_constructor:
+        elif func.parent is not None:
             return "%s.%s" % (self.format_page_name(func.parent), func.name)
         else:
             return func.name
 
-    def get_parameters(self, node):
-        skip = []
+    def format_page_name(self, node):
+        if isinstance(node, (ast.Field, ast.Property)):
+            return '%s.%s' % (self.format_page_name(node.parent), self.to_underscores(node))
+        else:
+            return DocFormatterIntrospectableBase.format_page_name(self, node)
+
+    def has_any_parameters(self, node):
+        return len(node.parameters) > 0 or \
+            node.retval.type.target_fundamental != 'none'
+
+    def get_in_parameters(self, node):
+        skip = set()
         for param in node.parameters:
             if param.direction == ast.PARAM_DIRECTION_OUT:
-                skip.append(param)
+                skip.add(param)
             if param.closure_name is not None:
-                skip.append(node.get_parameter(param.closure_name))
+                skip.add(node.get_parameter(param.closure_name))
             if param.destroy_name is not None:
-                skip.append(node.get_parameter(param.destroy_name))
+                skip.add(node.get_parameter(param.destroy_name))
             if isinstance(param.type, ast.Array) and param.type.length_param_name is not None:
-                skip.append(node.get_parameter(param.type.length_param_name))
+                skip.add(node.get_parameter(param.type.length_param_name))
 
         params = []
         for param in node.parameters:
@@ -568,6 +724,109 @@ class DocFormatterGjs(DocFormatterIntrospectableBase):
                 params.append(param)
         return params
 
+    def get_out_parameters(self, node):
+        skip = set()
+        for param in node.parameters:
+            if param.direction == ast.PARAM_DIRECTION_IN:
+                skip.add(param)
+            if param.closure_name is not None:
+                skip.add(node.get_parameter(param.closure_name))
+            if param.destroy_name is not None:
+                skip.add(node.get_parameter(param.destroy_name))
+            if isinstance(param.type, ast.Array) and param.type.length_param_name is not None:
+                skip.add(node.get_parameter(param.type.length_param_name))
+
+        params = []
+        if node.retval.type.target_fundamental != 'none':
+            name = 'return_value'
+            if node.retval.type.target_fundamental == 'gboolean':
+                name = 'ok'
+
+            params.append(ast.Parameter(name, node.retval.type,
+                                        ast.PARAM_DIRECTION_OUT))
+        for param in node.parameters:
+            if param not in skip:
+                params.append(param)
+
+        if len(params) == 1 and params[0].argname == 'return_value':
+            params[0].argname = 'Returns'
+
+        return params
+
+    def format_in_parameters(self, node):
+        in_params = self.get_in_parameters(node)
+        return ', '.join(('%s: %s' % (p.argname, self.format_type(p.type, True)))
+                         for p in in_params)
+
+    def format_out_parameters(self, node):
+        out_params = self.get_out_parameters(node)
+
+        if len(out_params) == 0:
+            return 'void'
+        elif len(out_params) == 1:
+            return self.format_type(out_params[0].type, True)
+        else:
+            return '[' + ', '.join(('%s: %s' % (p.argname, self.format_type(p.type, True)))
+                                   for p in out_params) + ']'
+
+    def field_is_writable(self, node):
+        if isinstance(node, ast.Field):
+            if node.type is None:
+                return False
+            if node.private:
+                return False
+            if node.type.target_fundamental not in \
+               (None, 'none', 'gpointer', 'utf8', 'filename', 'va_list'):
+                return True
+
+            resolved = self._transformer.lookup_typenode(node.type)
+            if resolved:
+                if isinstance(resolved, ast.Compound) and node.type.ctype[-1] != '*':
+                    return self._struct_is_simple(resolved)
+                elif isinstance(resolved, ast.Enum):
+                    return True
+            return False
+        else:
+            return True
+
+    def _struct_is_simple(self, node):
+        for f in node.fields:
+            if not self.field_is_writable(f):
+                return False
+        return True
+
+    def format_gboxed_constructor(self, node):
+        if node.namespace.name == 'GLib' and node.name == 'Variant':
+            return 'signature: String, value: Any'
+
+        zero_args_constructor = node.gjs_default_constructor
+        default_constructor = node.gjs_zero_args_constructor
+
+        can_allocate = zero_args_constructor is not None
+        if not can_allocate and isinstance(node, ast.Record):
+            can_allocate = self._struct_is_simple(node)
+
+        # Small lie: if can_allocate is False, and
+        # default_constructor is None, then you cannot
+        # construct the boxed in any way. But let's
+        # pretend you can with the regular constructor
+        if can_allocate or default_constructor is None:
+            if isinstance(node, ast.Compound):
+                fields = filter(self.field_is_writable, node.fields)
+                out = ''
+                if len(fields) > 0:
+                    out += "{\n"
+                    for f in fields:
+                        out += "    <link xref='%s.%s-%s'>%s</link>: value\n" % \
+                               (node.namespace.name, node.name, f.name, f.name)
+                    out += "}"
+                return out
+            else:
+                return ''
+        else:
+            construct_params = self.get_in_parameters(default_constructor)
+            return ', '.join(('%s: %s' % (p.argname, self.format_type(p.type)))
+                             for p in construct_params)
 
 LANGUAGES = {
     "c": DocFormatterC,
@@ -620,6 +879,14 @@ class DocWriter(object):
             return False
         if self._formatter.should_render_node(node):
             self._render_node(node, chain, output)
+
+            # hack: fields are not Nodes in the ast, so we don't
+            # see them in the visit. Handle them manually here
+            if isinstance(node, (ast.Compound, ast.Class)):
+                chain.append(node)
+                for f in node.fields:
+                    self._walk_node(output, f, chain)
+                chain.pop()
             return True
         return False
 
@@ -638,7 +905,8 @@ class DocWriter(object):
                                  node=node,
                                  page_id=page_id,
                                  page_kind=page_kind,
-                                 formatter=self._formatter)
+                                 formatter=self._formatter,
+                                 ast=ast)
 
         output_file_name = os.path.join(os.path.abspath(output),
                                         page_id + '.page')


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