summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Reiter <reiter.christoph@gmail.com>2018-08-14 07:03:14 +0000
committerChristoph Reiter <reiter.christoph@gmail.com>2018-08-14 07:03:14 +0000
commitf2be65b417b7179f5490c0735d053bd466f7f317 (patch)
tree8618529e44de000b363e4b2a93359c00d4bfae40
parent1f5c81048d762f4866137e1e3221d85b9b611d8b (diff)
parent48d348ac822234d898119d8a2118c2bb07145dd1 (diff)
downloadgobject-introspection-f2be65b417b7179f5490c0735d053bd466f7f317.tar.gz
Merge branch 'devdocs-format-rebase-v2' into 'master'
Add DevDocs formatting for GJS See merge request GNOME/gobject-introspection!57
-rw-r--r--Makefile-giscanner.am19
-rw-r--r--configure.ac13
-rw-r--r--giscanner/docmain.py5
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_doc.tmpl37
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_index.tmpl191
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_method.tmpl71
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_methods.tmpl4
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_properties.tmpl38
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_signals.tmpl22
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_staticmethods.tmpl4
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/_vfuncs.tmpl6
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/base.tmpl21
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/callback.tmpl3
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/class.tmpl1
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/default.tmpl34
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/enum.tmpl16
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/function.tmpl3
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/interface.tmpl1
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/method.tmpl1
-rw-r--r--giscanner/doctemplates/devdocs/Gjs/namespace.tmpl57
-rw-r--r--giscanner/docwriter.py416
-rw-r--r--giscanner/mdextensions.py13
-rw-r--r--giscanner/transformer.py1
23 files changed, 955 insertions, 22 deletions
diff --git a/Makefile-giscanner.am b/Makefile-giscanner.am
index 4f08934c..2879ab20 100644
--- a/Makefile-giscanner.am
+++ b/Makefile-giscanner.am
@@ -47,6 +47,7 @@ pkgpyexec_PYTHON = \
giscanner/introspectablepass.py \
giscanner/libtoolimporter.py \
giscanner/maintransformer.py \
+ giscanner/mdextensions.py \
giscanner/message.py \
giscanner/msvccompiler.py \
giscanner/pkgconfig.py \
@@ -105,7 +106,23 @@ nobase_dist_template_DATA = \
giscanner/doctemplates/mallard/Gjs/property.tmpl \
giscanner/doctemplates/mallard/Gjs/record.tmpl \
giscanner/doctemplates/mallard/Gjs/signal.tmpl \
- giscanner/doctemplates/mallard/Gjs/vfunc.tmpl
+ giscanner/doctemplates/mallard/Gjs/vfunc.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_doc.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_index.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_method.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_methods.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_properties.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_signals.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_staticmethods.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/_vfuncs.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/base.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/callback.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/class.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/default.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/enum.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/function.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/interface.tmpl \
+ giscanner/doctemplates/devdocs/Gjs/namespace.tmpl
_giscanner_la_CFLAGS = \
$(PYTHON_INCLUDES) \
diff --git a/configure.ac b/configure.ac
index 29bddde3..e8c23523 100644
--- a/configure.ac
+++ b/configure.ac
@@ -276,13 +276,20 @@ dnl an external dependency
AC_ARG_ENABLE(doctool,[ --disable-doctool disable g-ir-doc-tool ],,enable_doctool=auto)
AS_IF([ test x$enable_doctool != xno], [
AM_CHECK_PYMOD(mako,,have_python_mako=yes,have_python_mako=no)
+ AM_CHECK_PYMOD(markdown,,have_python_markdown=yes,have_python_markdown=no)
])
-AS_IF([ test x$enable_doctool = xauto && test x$have_python_mako = xyes ],
+AS_IF([ test x$enable_doctool = xauto &&
+ test x$have_python_mako = xyes &&
+ test x$have_python_markdown = xyes ],
[ enable_doctool=yes ],
- [ test x$enable_doctool = xauto && test x$have_python_mako = xno ],
+ [ test x$enable_doctool = xauto &&
+ (test x$have_python_mako = xno ||
+ test x$have_python_markdown = xno) ],
[ enable_doctool=no ],
[ test x$enable_doctool = xyes && test x$have_python_mako = xno ],
- [ AC_MSG_ERROR([Python mako module not found]) ])
+ [ AC_MSG_ERROR([Python mako module not found]) ],
+ [ test x$enable_doctool = xyes && test x$have_python_markdown = xno ],
+ [ AC_MSG_ERROR([Python markdown module not found]) ])
AM_CONDITIONAL(BUILD_DOCTOOL, test x$enable_doctool != xno)
# Glib documentation
diff --git a/giscanner/docmain.py b/giscanner/docmain.py
index 966b33c2..f184014b 100644
--- a/giscanner/docmain.py
+++ b/giscanner/docmain.py
@@ -32,7 +32,7 @@ from .docwriter import DocWriter
from .sectionparser import generate_sections_file, write_sections_file
from .transformer import Transformer
-FORMATS = ('mallard', 'sections')
+FORMATS = ('devdocs', 'mallard', 'sections')
def doc_main(args):
@@ -61,6 +61,9 @@ def doc_main(args):
args = parser.parse_args(args[1:])
if not args.output:
raise SystemExit("missing output parameter")
+ if args.format not in FORMATS:
+ raise SystemExit("Unknown output format %s (supported: %s)" %
+ (args.format, ", ".join(FORMATS)))
if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR']
diff --git a/giscanner/doctemplates/devdocs/Gjs/_doc.tmpl b/giscanner/doctemplates/devdocs/Gjs/_doc.tmpl
new file mode 100644
index 00000000..dbdb8259
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_doc.tmpl
@@ -0,0 +1,37 @@
+<%def name="format_documentation(node)">
+ % if node.version:
+ <p class="since-note">
+ New in version ${node.version}.
+ ${formatter.format_inline(node, node.version_doc)}
+ </p>
+ % endif
+ % if node.deprecated:
+ <p class="deprecated-note">
+ Deprecated since ${node.deprecated}.
+ ${formatter.format_inline(node, node.deprecated_doc)}
+ </p>
+ % endif
+ % if node.stability:
+ ## Not sure what this looks like in the wild
+ <p class="stability-note">
+ Stability: ${node.stability}.
+ ${formatter.format_inline(node, node.stability_doc)}
+ </p>
+ % endif
+
+ % if node.doc:
+ ${formatter.format(node, node.doc)}
+ % endif
+</%def>
+
+<%def name="deprecated_class(node)">
+ % if node.deprecated:
+ deprecated
+ % endif
+</%def>
+
+<%def name="introspectable(node)">
+ % if getattr(node, "introspectable", True):
+ ${caller.body()}
+ % endif
+</%def>
diff --git a/giscanner/doctemplates/devdocs/Gjs/_index.tmpl b/giscanner/doctemplates/devdocs/Gjs/_index.tmpl
new file mode 100644
index 00000000..f3d588ae
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_index.tmpl
@@ -0,0 +1,191 @@
+<%namespace name="doc" file="_doc.tmpl"/>
+
+<%
+ ancestors = []
+ if isinstance(node, ast.Class):
+ ancestors = formatter.get_inheritable_types(node)
+
+ fields = getattr(node, 'fields', [])
+ extra_fields = [getattr(f.anonymous_node, 'fields', []) for f in fields
+ if f.anonymous_node is not None]
+ extra_fields = [field for sublist in extra_fields for field in sublist]
+ non_private_fields = [f for f in fields + extra_fields
+ if not formatter.is_private_field(node, f)]
+
+ def get_ancestor_counts(*kinds):
+ counts = {}
+ for a in ancestors:
+ count = 0
+ for kind in kinds:
+ if kind == 'fields':
+ count += len(non_private_fields)
+ else:
+ count += len(getattr(a, kind, []))
+ if count:
+ counts[a] = count
+ return counts
+
+ def should_render(*kinds):
+ has_nonempty = False
+ for kind in kinds:
+ if kind == 'fields':
+ if non_private_fields:
+ has_nonempty = True
+ elif getattr(node, kind, []):
+ has_nonempty = True
+ return has_nonempty or get_ancestor_counts(*kinds)
+%>
+
+<%def name="inherited(*kinds)">
+ <% counts = get_ancestor_counts(*kinds) %>
+ % if counts:
+ <%
+ links = ', '.join(['{} ({})'.format(formatter.format_xref(a), count)
+ for a, count in counts.items()])
+ %>
+ ${formatter.format(node, '**Inherited:** ' + links)}
+ % endif
+</%def>
+
+<%def name="format_function_cell(m)">
+ <td class="${doc.deprecated_class(m)}">
+ <a href="#${formatter.make_anchor(m)}">
+ ${formatter.format_function_name(m)}<!-- no space
+ --></a><!-- no space
+ -->(${formatter.format_in_parameters(m)})
+ </td>
+</%def>
+
+% if should_render('static_methods', 'constructors', 'methods'):
+ <h2 id="index-methods">Methods</h2>
+ ${inherited('static_methods', 'constructors', 'methods')}
+ <%
+ static_methods = getattr(node, 'static_methods', []) + getattr(node, 'constructors', [])
+ methods = static_methods + getattr(node, 'methods', [])
+ %>
+ % if methods:
+ <table class="index">
+ <tbody>
+ % for m in methods:
+ <%doc:introspectable node="${m}">
+ <tr>
+ % if m in static_methods:
+ <td class="static-method-indicator">static</td>
+ % else:
+ <td></td>
+ % endif
+ ${format_function_cell(m)}
+ </tr>
+ </%doc:introspectable>
+ % endfor
+ </tbody>
+ </table>
+ % endif
+% endif
+
+% if should_render('virtual_methods'):
+ <h2 id="index-vfuncs">Virtual methods</h2>
+ ${inherited('virtual_methods')}
+ % if getattr(node, 'virtual_methods', []):
+ <table>
+ <tbody>
+ % for m in node.virtual_methods:
+ <%doc:introspectable node="${m}">
+ <tr>
+ ${format_function_cell(m)}
+ </tr>
+ </%doc:introspectable>
+ % endfor
+ </tbody>
+ </table>
+ % endif
+% endif
+
+% if should_render('properties'):
+ <h2 id="index-properties">Properties</h2>
+ ${inherited('properties')}
+ % if getattr(node, 'properties', []):
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Flags</th>
+ </tr>
+ </thead>
+ <tbody>
+ % for p in node.properties:
+ <%doc:introspectable node="${p}">
+ <tr>
+ <td class="${doc.deprecated_class(p)}">
+ <a href="#${formatter.make_anchor(p)}">${p.name}</a>
+ </td>
+ <td>${formatter.format_type(p.type)}</td>
+ <td>${formatter.format_property_flags(p, abbrev=True)}</td>
+ </tr>
+ </%doc:introspectable>
+ % endfor
+ </tbody>
+ </table>
+ % endif
+% endif
+
+% if should_render('signals'):
+ <h2 id="index-signals">Signals</h2>
+ ${inherited('signals')}
+ % if getattr(node, 'signals', []):
+ <table>
+ <tbody>
+ % for s in node.signals:
+ <%doc:introspectable node="${s}">
+ <tr>
+ <td class="${doc.deprecated_class(s)}">
+ <a href="#${formatter.make_anchor(s)}">${s.name}</a><!-- no space
+ -->(${formatter.format_in_parameters(s)})
+ </td>
+ </tr>
+ </%doc:introspectable>
+ % endfor
+ </tbody>
+ </table>
+ % endif
+% endif
+
+% if should_render('fields'):
+ <h2 id="index-fields">Fields</h2>
+ ${inherited('fields')}
+ % if non_private_fields:
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Access</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ % for f in non_private_fields:
+ <%doc:introspectable node="${f}">
+ <tr>
+ <td class="${doc.deprecated_class(f)}">
+ <span class="entry" href="#${formatter.make_anchor(f)}">
+ ${f.name}
+ </span>
+ </td>
+ <td>${formatter.format_type(f.type)}</td>
+ <td>${formatter.format_property_flags(f, abbrev=True)}</td>
+ ## Fields almost never warrant a detailed entry, we'll just make this
+ ## the entry to be indexed by DevDocs
+ <td>
+ % if f.doc:
+ ${formatter.format_inline(node, f.doc)}
+ % endif
+ </td>
+ </tr>
+ </%doc:introspectable>
+ % endfor
+ </tbody>
+ </table>
+ % endif
+% endif
diff --git a/giscanner/doctemplates/devdocs/Gjs/_method.tmpl b/giscanner/doctemplates/devdocs/Gjs/_method.tmpl
new file mode 100644
index 00000000..57520465
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_method.tmpl
@@ -0,0 +1,71 @@
+<%namespace name="doc" file="_doc.tmpl"/>
+
+<%def name="describe_parameters(m, static=False, virtual=False)">
+ <dl>
+ % if static:
+ <dt>Type:</dt>
+ <dd>Static</dd>
+ % elif virtual:
+ <dt>Type:</dt>
+ <dd>Virtual</dd>
+ % endif
+ <%
+ in_params = formatter.get_in_parameters(m)
+ out_params = formatter.get_out_parameters(m)
+ %>
+ % if in_params:
+ <dt>Parameters:</dt>
+ <dd>
+ <ul>
+ % for p in in_params:
+ <li>
+ <strong>${p.argname}</strong>
+ (<code>${formatter.format_type(p.type)}</code>)
+ % if p.doc:
+ &mdash; ${formatter.format_inline(m, p.doc)}
+ % endif
+ </li>
+ % endfor
+ </ul>
+ </dd>
+ % endif
+ % if out_params:
+ <dt>Returns:</dt>
+ <dd>
+ <ul>
+ % for p in out_params:
+ <li>
+ % if len(out_params) > 1:
+ <strong>${p.argname}</strong>
+ % endif
+ (<code>${formatter.format_type(p.type)}</code>)
+ % if p.doc:
+ &mdash; ${formatter.format_inline(m, p.doc)}
+ % endif
+ </li>
+ % endfor
+ </ul>
+ </dd>
+ % endif
+ % if m.throws:
+ <dt>Throws exception:</dt>
+ <dd>Yes</dd>
+ % endif
+ </dl>
+</%def>
+
+<%def name="method(m, static=False, virtual=False)">
+ <%doc:introspectable node="${m}">
+ <% invocation = ", ".join(map(lambda p: p.argname, m.parameters)) %>
+
+ <h3>
+ <span class="entry ${get_node_kind(m)} ${doc.deprecated_class(m)}"
+ id="${formatter.make_anchor(m)}">
+ ${formatter.format_function_name(m)}<!-- no space
+ --></span><!-- no space
+ -->(${formatter.format_in_parameters(m)})
+ </h3>
+ ${describe_parameters(m, static, virtual)}
+ ${doc.format_documentation(m)}
+ </%doc:introspectable>
+</%def>
diff --git a/giscanner/doctemplates/devdocs/Gjs/_methods.tmpl b/giscanner/doctemplates/devdocs/Gjs/_methods.tmpl
new file mode 100644
index 00000000..e876cd68
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_methods.tmpl
@@ -0,0 +1,4 @@
+<%namespace name="method" file="_method.tmpl"/>
+% for m in getattr(node, 'methods', []):
+ ${method.method(m)}
+% endfor
diff --git a/giscanner/doctemplates/devdocs/Gjs/_properties.tmpl b/giscanner/doctemplates/devdocs/Gjs/_properties.tmpl
new file mode 100644
index 00000000..a7054727
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_properties.tmpl
@@ -0,0 +1,38 @@
+<%namespace name="doc" file="_doc.tmpl"/>
+<%!
+ def dash_to_underscore(string):
+ return '_'.join(string.split('-'))
+
+ def dash_to_camel(string):
+ words = string.split('-')
+ return ''.join([words[0]] + [word.title() for word in words[1:]])
+%>
+% if getattr(node, 'properties', []):
+ <h2>Property Details</h2>
+ % for p in node.properties:
+ <%doc:introspectable node="${p}">
+ <h3 class="entry property ${doc.deprecated_class(p)}"
+ id="${formatter.make_anchor(p)}">
+ ${p.name | dash_to_underscore}
+ </h3>
+ <dl>
+ % if p.name.lower() != p.name:
+ <dt>Names</dt>
+ <dd>
+ <code>${p.name}</code>, <code>${p.name | dash_to_underscore}</code>,
+ <code>${p.name | dash_to_camel}</code>
+ </dd>
+ % endif
+ <dt>Type</dt>
+ <dd><code>${formatter.format_type(p.type)}</code></dd>
+ ##<dt>Default value</dt>
+ ##<dd>Not currently stored in GIR</dd>
+ <dt>Flags</dt>
+ <dd>${formatter.format_property_flags(p)}</dd>
+ </dl>
+ % if p.doc:
+ ${doc.format_documentation(p)}
+ % endif
+ </%doc:introspectable>
+ % endfor
+% endif
diff --git a/giscanner/doctemplates/devdocs/Gjs/_signals.tmpl b/giscanner/doctemplates/devdocs/Gjs/_signals.tmpl
new file mode 100644
index 00000000..cda46bd5
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_signals.tmpl
@@ -0,0 +1,22 @@
+<%namespace name="doc" file="_doc.tmpl"/>
+<%namespace name="method" file="_method.tmpl"/>
+% if getattr(node, 'signals', []):
+ <h2>Signal Details</h2>
+ % for s in node.signals:
+ <%doc:introspectable node="${s}">
+ <h3>
+ <span class="entry signal ${doc.deprecated_class(s)}"
+ id="${formatter.make_anchor(s)}">
+ ${s.name}<!-- no space
+ --></span><!--
+ -->(${formatter.format_in_parameters(s)})
+ </h3>
+ <dl>
+ <dt>Flags</dt>
+ <dd>${formatter.format_signal_flags(s)}</dd>
+ ${method.describe_parameters(s)}
+ </dl>
+ ${doc.format_documentation(s)}
+ </%doc:introspectable>
+ % endfor
+% endif
diff --git a/giscanner/doctemplates/devdocs/Gjs/_staticmethods.tmpl b/giscanner/doctemplates/devdocs/Gjs/_staticmethods.tmpl
new file mode 100644
index 00000000..dcd542e1
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_staticmethods.tmpl
@@ -0,0 +1,4 @@
+<%namespace name="method" file="_method.tmpl"/>
+% for m in getattr(node, 'static_methods', []) + getattr(node, 'constructors', []):
+ ${method.method(m, static=True)}
+% endfor
diff --git a/giscanner/doctemplates/devdocs/Gjs/_vfuncs.tmpl b/giscanner/doctemplates/devdocs/Gjs/_vfuncs.tmpl
new file mode 100644
index 00000000..2966ede4
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/_vfuncs.tmpl
@@ -0,0 +1,6 @@
+<%namespace name="method" file="_method.tmpl"/>
+% if getattr(node, 'virtual_methods', []):
+ % for m in node.virtual_methods:
+ ${method.method(m, virtual=True)}
+ % endfor
+% endif
diff --git a/giscanner/doctemplates/devdocs/Gjs/base.tmpl b/giscanner/doctemplates/devdocs/Gjs/base.tmpl
new file mode 100644
index 00000000..913fa0cc
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/base.tmpl
@@ -0,0 +1,21 @@
+<%namespace name="doc" file="_doc.tmpl"/>
+<html>
+<body>
+ <section>
+ <h1 class="${page_kind} ${doc.deprecated_class(node)} ${self.extra_class()}">
+ ${formatter.format_page_name(node)}
+ </h1>
+ <%include file="_index.tmpl"/>
+ <h2>Details</h2>
+ ${doc.format_documentation(node)}
+ <%include file="_staticmethods.tmpl"/>
+ <%include file="_methods.tmpl"/>
+ <%include file="_vfuncs.tmpl"/>
+ <%include file="_signals.tmpl"/>
+ <%include file="_properties.tmpl"/>
+ ${self.body()}
+ </section>
+</body>
+</html>
+
+<%def name="extra_class()"/>
diff --git a/giscanner/doctemplates/devdocs/Gjs/callback.tmpl b/giscanner/doctemplates/devdocs/Gjs/callback.tmpl
new file mode 100644
index 00000000..2795ee3c
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/callback.tmpl
@@ -0,0 +1,3 @@
+<%namespace name="method" file="_method.tmpl"/>
+<%inherit file="base.tmpl"/>
+${method.describe_parameters(node)}
diff --git a/giscanner/doctemplates/devdocs/Gjs/class.tmpl b/giscanner/doctemplates/devdocs/Gjs/class.tmpl
new file mode 100644
index 00000000..9d2b5238
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/class.tmpl
@@ -0,0 +1 @@
+<%inherit file="base.tmpl"/>
diff --git a/giscanner/doctemplates/devdocs/Gjs/default.tmpl b/giscanner/doctemplates/devdocs/Gjs/default.tmpl
new file mode 100644
index 00000000..4b08adff
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/default.tmpl
@@ -0,0 +1,34 @@
+<%inherit file="base.tmpl"/>
+% if isinstance(node, ast.Constant):
+ <dl>
+ <dt>Value</dt>
+ <dd>
+ <code data-mime="application/javascript"><!--
+ -->${formatter.format_value(node)}</code>
+ </dd>
+ </dl>
+% endif
+% if isinstance(node, ast.Alias):
+ <dl>
+ <dt>Equivalent Type</dt>
+ <dd>
+ <code data-mime="application/javascript">
+ % if node.target.target_fundamental:
+ ${formatter.format_fundamental_type(node.target.target_fundamental)}
+ % else:
+ ${node.target.target_giname}
+ % endif
+ </code>
+ </dd>
+ </dl>
+% endif
+
+## This should belong in get_node_kind(), but we don't want to change the way
+## all the other templates work.
+<%def name="extra_class()">
+ % if isinstance(node, ast.Constant):
+ constant
+ % elif isinstance(node, ast.Alias):
+ alias
+ % endif
+</%def>
diff --git a/giscanner/doctemplates/devdocs/Gjs/enum.tmpl b/giscanner/doctemplates/devdocs/Gjs/enum.tmpl
new file mode 100644
index 00000000..a66cbefa
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/enum.tmpl
@@ -0,0 +1,16 @@
+<%inherit file="base.tmpl"/>
+<ul>
+% for m in node.members:
+ <li>
+ <code>
+ <span class="entry" id="${formatter.make_anchor(m)}">
+ ${m.name.upper()}
+ </span>
+ = ${m.value}
+ </code>
+ % if m.doc:
+ &mdash; ${formatter.format_inline(node, m.doc)}
+ % endif
+ </li>
+% endfor
+</ul>
diff --git a/giscanner/doctemplates/devdocs/Gjs/function.tmpl b/giscanner/doctemplates/devdocs/Gjs/function.tmpl
new file mode 100644
index 00000000..2795ee3c
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/function.tmpl
@@ -0,0 +1,3 @@
+<%namespace name="method" file="_method.tmpl"/>
+<%inherit file="base.tmpl"/>
+${method.describe_parameters(node)}
diff --git a/giscanner/doctemplates/devdocs/Gjs/interface.tmpl b/giscanner/doctemplates/devdocs/Gjs/interface.tmpl
new file mode 100644
index 00000000..9d2b5238
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/interface.tmpl
@@ -0,0 +1 @@
+<%inherit file="base.tmpl"/>
diff --git a/giscanner/doctemplates/devdocs/Gjs/method.tmpl b/giscanner/doctemplates/devdocs/Gjs/method.tmpl
new file mode 100644
index 00000000..2c997c09
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/method.tmpl
@@ -0,0 +1 @@
+<%inherit file="function.tmpl"/> \ No newline at end of file
diff --git a/giscanner/doctemplates/devdocs/Gjs/namespace.tmpl b/giscanner/doctemplates/devdocs/Gjs/namespace.tmpl
new file mode 100644
index 00000000..8f5a4e7e
--- /dev/null
+++ b/giscanner/doctemplates/devdocs/Gjs/namespace.tmpl
@@ -0,0 +1,57 @@
+<%def name="get_types(n, types)">
+ <% nodes = [] %>
+ % for a in n.values():
+ % if isinstance(a, types):
+ % if formatter.should_render_node(a):
+ <% nodes.append(a) %>
+ % endif
+ % endif
+ % endfor
+ <% return nodes %>
+</%def>
+
+<%def name="render_list(nodes)">
+ <ul>
+ % for a in nodes:
+ <li>${formatter.format_inline(a, formatter.format_xref(a))}</li>
+ % endfor
+ </ul>
+</%def>
+
+<html>
+<body>
+ <section>
+ <h1 class="namespace">${node.name}</h1>
+ </section>
+
+ <% nodes = get_types(node, (ast.Class, ast.Interface)) %>
+ % if len(nodes) > 0:
+ <h1>Classes</h1>
+ ${render_list(nodes)}
+ % endif
+
+ <% nodes = get_types(node, (ast.Enum)) %>
+ % if len(nodes) > 0:
+ <h1>Enums</h1>
+ ${render_list(nodes)}
+ % endif
+
+ <% nodes = get_types(node, (ast.Function)) %>
+ % if len(nodes) > 0:
+ <h1>Functions</h1>
+ ${render_list(nodes)}
+ % endif
+
+ <% nodes = get_types(node, (ast.Constant)) %>
+ % if len(nodes) > 0:
+ <h1>Constants</h1>
+ ${render_list(nodes)}
+ % endif
+
+ <% nodes = get_types(node, (ast.Property)) %>
+ % if len(nodes) > 0:
+ <h1>Properties</h1>
+ ${render_list(nodes)}
+ % endif
+</body>
+</html>
diff --git a/giscanner/docwriter.py b/giscanner/docwriter.py
index d9c2ed57..d49446a0 100644
--- a/giscanner/docwriter.py
+++ b/giscanner/docwriter.py
@@ -28,13 +28,42 @@ from __future__ import unicode_literals
import os
import re
+import sys
import tempfile
from xml.sax import saxutils
from mako.lookup import TemplateLookup
+import markdown
+from markdown.extensions.headerid import HeaderIdExtension
from . import ast, xmlwriter
from .utils import to_underscores
+from .mdextensions import InlineMarkdown
+
+# Freely inspired from
+# https://github.com/GNOME/yelp-xsl/blob/master/js/syntax.html
+language_mimes = {
+ "bash-script": "application/x-shellscript",
+ "shell": "application/x-shellscript",
+ "csharp": "text/x-csharp",
+ "css": "text/css",
+ "diff": "text/xpatch",
+ "html": "text/html",
+ "java": "text/x-java",
+ "javascript": "application/javascript",
+ "lisp": "text/x-scheme",
+ "lua": "text-x-lua",
+ "c": "text/x-csrc",
+ "c++": "text/x-c++src",
+ "pascal": "text/x-pascal",
+ "perl": "application/x-perl",
+ "php": "application/x-php",
+ "plain": "text/plain",
+ "python": "text/x-python",
+ "ruby": "application/x-ruby",
+ "sql": "text/x-sql",
+ "yaml": "application/x-yaml",
+}
def make_page_id(node, recursive=False):
@@ -166,6 +195,15 @@ class DocstringScanner(TemplatedScanner):
specs = [
('!alpha', r'[a-zA-Z0-9_]+'),
('!alpha_dash', r'[a-zA-Z0-9_-]+'),
+ ('code_start_with_language',
+ r'\|\[\<!\-\-\s*language\s*\=\s*\"<<language_name:alpha>>\"\s*\-\-\>'),
+ ('code_start', r'\|\['),
+ ('code_end', r'\]\|'),
+ ('html_code_start', r'<code(.*?)>'),
+ ('html_code_end', r'</code>'),
+ ('markdown_code_toggle', r'\`'),
+ ('markdown_attr_start', r'\{'),
+ ('markdown_attr_end', r'\}'),
('property', r'#<<type_name:alpha>>:(<<property_name:alpha_dash>>)'),
('signal', r'#<<type_name:alpha>>::(<<signal_name:alpha_dash>>)'),
('type_name', r'#(<<type_name:alpha>>)'),
@@ -181,10 +219,18 @@ class DocFormatter(object):
def __init__(self, transformer):
self._transformer = transformer
self._scanner = DocstringScanner()
+ # If we are processing a code block as defined by
+ # https://wiki.gnome.org/Projects/GTK%2B/DocumentationSyntax/Markdown
+ # we won't insert paragraphs and will respect new lines.
+ self._processing_code = False
+ self._processing_attr = False
def escape(self, text):
return saxutils.escape(text)
+ def unescape(self, text):
+ return saxutils.unescape(text)
+
def should_render_node(self, node):
if getattr(node, "private", False):
return False
@@ -203,11 +249,9 @@ class DocFormatter(object):
if doc is None:
return ''
- result = ''
- for para in doc.split('\n\n'):
- result += ' <p>'
- result += self.format_inline(node, para)
- result += '</p>'
+ result = '<p>'
+ result += self.format_inline(node, doc)
+ result += '</p>'
return result
def _resolve_type(self, ident):
@@ -239,6 +283,8 @@ class DocFormatter(object):
raise KeyError("Could not find %s" % (name, ))
def _process_other(self, node, match, props):
+ if self._processing_code:
+ return match
return self.escape(match)
def _process_property(self, node, match, props):
@@ -266,11 +312,20 @@ class DocFormatter(object):
return self.format_xref(signal)
def _process_type_name(self, node, match, props):
- type_ = self._resolve_type(props['type_name'])
- if type_ is None:
+ if self._processing_attr:
return match
- return self.format_xref(type_)
+ ident = props['type_name']
+ type_ = self._resolve_type(ident)
+ plural = False
+ if type_ is None:
+ singularized = ident.rstrip("s") # Try to remove plural
+ type_ = self._resolve_type(singularized)
+ plural = True
+ if type_ is None:
+ return match
+
+ return self.format_xref(type_, pluralize=plural)
def _process_enum_value(self, node, match, props):
member_name = props['member_name']
@@ -301,6 +356,79 @@ class DocFormatter(object):
return self.format_xref(func)
+ # FIXME: the four spaces after newlines in the following functions are to
+ # keep Markdown happy. We pass the documentation string first through this
+ # templated scanner, which converts |[ ]| to <pre></pre>. Then in the case
+ # of DevDocs output, we pass the resulting string through Markdown; but
+ # Markdown will not respect the <pre> element and will treat the code as
+ # markup, converting asterisks into <em> etc. Putting four spaces at the
+ # start of each line makes Markdown recognize the code as code without
+ # affecting the normal HTML output too much.
+ #
+ # A better solution would be to replace DocstringScanner by Markdown
+ # entirely, implementing the custom markup with Markdown extensions.
+ #
+ # UPDATE: As a temporary fix for code blocks we will convert directly to ``` syntax.
+ #
+ # NOTES:
+ # _process_markdown_code_toggle:
+ # Whenever we encounter ` we need to toggle whether we are escaping text as text inside
+ # inline code blocks is unescaped
+ # _process_markdown_attr_(start|end):
+ # Whenever we encounter { or } we must stop parsing type names as curly braces are used for
+ # attributes in GIR files in addition to type declarations.
+ # _process_html_code_(start|end):
+ # Whenever we encounter an HTML <code> block we must stop escaping text.
+ #
+ # TODO: Convert to markdown extensions.
+
+ def _process_markdown_code_toggle(self, node, match, props):
+ self._processing_code = not self._processing_code
+ return match
+
+ def _process_markdown_attr_start(self, node, match, props):
+ if not self._processing_code:
+ self._processing_attr = True
+ return match
+
+ def _process_markdown_attr_end(self, node, match, props):
+ if not self._processing_code:
+ self._processing_attr = False
+ return match
+
+ def _process_html_code_start(self, node, match, props):
+ self._processing_code = True
+ return match
+
+ def _process_html_code_end(self, node, match, props):
+ self._processing_code = False
+ return match
+
+ def _process_code_start(self, node, match, props):
+ self._processing_code = True
+ return '</p>\n```\n'
+
+ def _process_code_start_with_language(self, node, match, props):
+ self._processing_code = True
+ try:
+ return '</p>\n```' + props["language_name"].lower() + '\n'
+ except KeyError:
+ return '</p>\n```\n'
+
+ def _process_code_end(self, node, match, props):
+ self._processing_code = False
+ return '\n```\n<p>'
+
+ def _process_new_line(self, node, match, props):
+ if self._processing_code:
+ return '\n'
+ return '\n'
+
+ def _process_new_paragraph(self, node, match, props):
+ if self._processing_code:
+ return '\n\n'
+ return "</p><p>"
+
def _process_token(self, node, tok):
kind, match, props = tok
@@ -312,6 +440,16 @@ class DocFormatter(object):
'enum_value': self._process_enum_value,
'parameter': self._process_parameter,
'function_call': self._process_function_call,
+ 'code_start': self._process_code_start,
+ 'code_start_with_language': self._process_code_start_with_language,
+ 'code_end': self._process_code_end,
+ 'html_code_start': self._process_html_code_start,
+ 'html_code_end': self._process_html_code_end,
+ 'markdown_code_toggle': self._process_markdown_code_toggle,
+ 'markdown_attr_start': self._process_markdown_attr_start,
+ 'markdown_attr_end': self._process_markdown_attr_end,
+ 'new_line': self._process_new_line,
+ 'new_paragraph': self._process_new_paragraph,
}
return dispatch[kind](node, match, props)
@@ -336,6 +474,9 @@ class DocFormatter(object):
def format_type(self, type_, link=False):
raise NotImplementedError
+ def format_value(self, node):
+ raise NotImplementedError
+
def format_page_name(self, node):
if isinstance(node, ast.Namespace):
return node.name
@@ -352,33 +493,41 @@ class DocFormatter(object):
else:
return make_page_id(node)
- def format_xref(self, node, **attrdict):
+ def format_xref(self, node, pluralize=False, **attrdict):
if node is None or not hasattr(node, 'namespace'):
attrs = [('xref', 'index')] + list(sorted(attrdict.items()))
return xmlwriter.build_xml_tag('link', attrs)
elif isinstance(node, ast.Member):
# Enum/BitField members are linked to the main enum page.
- return self.format_xref(node.parent, **attrdict) + '.' + node.name
+ return self.format_xref(node.parent, pluralize=pluralize, **attrdict) + '.' + node.name
elif node.namespace is self._transformer.namespace:
- return self.format_internal_xref(node, attrdict)
+ return self.format_internal_xref(node, attrdict, pluralize=pluralize)
else:
- return self.format_external_xref(node, attrdict)
+ return self.format_external_xref(node, attrdict, pluralize=pluralize)
- def format_internal_xref(self, node, attrdict):
+ def format_internal_xref(self, node, attrdict, pluralize=False):
attrs = [('xref', make_page_id(node))] + list(sorted(attrdict.items()))
- return xmlwriter.build_xml_tag('link', attrs)
+ if not pluralize:
+ return xmlwriter.build_xml_tag('link', attrs)
+ else:
+ return xmlwriter.build_xml_tag('link', attrs, make_page_id(node) +
+ "s")
- def format_external_xref(self, node, attrdict):
+ def format_external_xref(self, node, attrdict, pluralize=False):
ns = node.namespace
attrs = [('href', '../%s-%s/%s.html' % (ns.name, str(ns.version),
make_page_id(node)))]
attrs += list(sorted(attrdict.items()))
- return xmlwriter.build_xml_tag('link', attrs, self.format_page_name(node))
+ if not pluralize:
+ return xmlwriter.build_xml_tag('link', attrs, self.format_page_name(node))
+ else:
+ return xmlwriter.build_xml_tag('link', attrs,
+ self.format_page_name(node) + "s")
def field_is_writable(self, field):
return True
- def format_property_flags(self, property_, construct_only=False):
+ def format_property_flags(self, property_, construct_only=False, abbrev=False):
flags = []
if property_.readable and not construct_only:
@@ -392,6 +541,23 @@ class DocFormatter(object):
if property_.construct_only:
flags.append("Construct Only")
+ if abbrev:
+ return "/".join([''.join([word[0] for word in flag.lower().split()])
+ for flag in flags])
+ return " / ".join(flags)
+
+ def format_signal_flags(self, signal):
+ flags = []
+ if signal.action:
+ flags.append("Action")
+ if signal.detailed:
+ flags.append("Detailed")
+ if signal.no_hooks:
+ flags.append("No Hooks")
+ if signal.no_recurse:
+ flags.append("No Recurse")
+ if signal.when:
+ flags.append("Run " + signal.when.capitalize())
return " / ".join(flags)
def to_underscores(self, node):
@@ -399,6 +565,8 @@ class DocFormatter(object):
return node.name.replace('-', '_')
elif node.name:
return to_underscores(node.name)
+ elif isinstance(node, ast.Function) and node.moved_to:
+ return to_underscores(node.moved_to)
elif isinstance(node, ast.Callback):
return 'callback'
elif isinstance(node, ast.Union):
@@ -422,6 +590,51 @@ class DocFormatter(object):
parent_chain.reverse()
return parent_chain
+ def get_inheritable_types(self, node):
+ """Return an ast.Node object for each type (ast.Class and ast.Interface
+ types) from which an ast.Class @node might inherit methods, properties,
+ and signals."""
+
+ assert isinstance(node, ast.Class)
+
+ parent_chain = self.get_class_hierarchy(node)
+ types = []
+ for p in parent_chain:
+ types += [self._transformer.lookup_typenode(t) for t in p.interfaces]
+ types += [t for t in parent_chain if t is not node]
+ return types
+
+ def is_private_field(self, node, f):
+ """Returns whether @f is a private field of @node (including a heuristic
+ that tries to determine whether the field is the parent instance field
+ or a private pointer but not marked as such.)"""
+
+ if f.private:
+ return True
+ if f.anonymous_node:
+ return True
+ if f.name == 'g_type_instance':
+ return True # this field on GObject is not exposed
+
+ field_typenode = self._transformer.lookup_typenode(f.type)
+ if not field_typenode:
+ return False
+
+ if getattr(field_typenode, 'disguised', False):
+ return True # guess that it's a pointer to a private struct
+ # this also catches fields of type GdkAtom, since that is disguised
+ # as well. Not sure whether that's correct or not.
+
+ if not isinstance(node, ast.Class):
+ return False # parent instance heuristics only apply to classes
+
+ if node.parent_type:
+ parent_typenode = self._transformer.lookup_typenode(node.parent_type)
+ if field_typenode == parent_typenode:
+ return True # guess that it's a parent instance field
+
+ return False
+
def format_prerequisites(self, node):
assert isinstance(node, ast.Interface)
@@ -721,6 +934,8 @@ class DocFormatterGjs(DocFormatterIntrospectableBase):
return "void"
elif type_.target_giname is not None:
giname = type_.target_giname
+ if giname == 'Gdk.Atom':
+ return 'String'
if giname in ('GLib.ByteArray', 'GLib.Bytes'):
return 'ByteArray'
if giname == 'GObject.Value':
@@ -889,7 +1104,172 @@ class DocFormatterGjs(DocFormatterIntrospectableBase):
return ', '.join(('%s: %s' % (p.argname, self.format_type(p.type)))
for p in construct_params)
+
+class DevDocsFormatterGjs(DocFormatterGjs):
+ output_format = "devdocs"
+ output_extension = ".html"
+
+ def _is_static_method(self, node):
+ if not hasattr(node.parent, "static_methods"):
+ return False
+ return node in node.parent.static_methods
+
+ def should_render_node(self, node):
+ # For DevDocs, we only want to render the top-level nodes.
+ if isinstance(node, (ast.Compound, ast.Boxed)):
+ self.resolve_gboxed_constructor(node)
+
+ if not super(DevDocsFormatterGjs, self).should_render_node(node):
+ return False
+
+ if isinstance(node, ast.Function) and not node.is_method and \
+ not node.is_constructor and not self._is_static_method(node):
+ return True # module-level function
+ toplevel_types = [ast.Alias, ast.Bitfield, ast.Boxed, ast.Callback,
+ ast.Class, ast.Constant, ast.Enum, ast.Interface, ast.Namespace,
+ ast.Record, ast.Union]
+ for ast_type in toplevel_types:
+ if isinstance(node, ast_type):
+ return True
+
+ return False
+
+ def format_fundamental_type(self, name):
+ # Don't specify the C type after Number as the Mallard docs do; it's
+ # confusing to GJS newbies.
+ if name in ["gint8", "guint8", "gint16", "guint16", "gint32", "guint32",
+ "gchar", "guchar", "gshort", "gint", "guint", "gfloat",
+ "gdouble", "gsize", "gssize", "gintptr", "guintptr",
+ "glong", "gulong", "gint64", "guint64", "long double",
+ "long long", "unsigned long long"]:
+ return "Number" # gsize and up cannot fully be represented in GJS
+ if name in ["none", "gpointer"]:
+ return "void"
+ if name in ["utf8", "gunichar", "filename"]:
+ return "String"
+ if name == "gboolean":
+ return "Boolean"
+ if name == "GType":
+ return "GObject.Type"
+ if name == "GVariant":
+ return "GLib.Variant"
+ return name
+
+ def format_value(self, node):
+ # Constants only have fundamental types?
+ type_ = node.value_type.target_fundamental
+ if type_ in ["utf8", "gunichar", "filename"]:
+ return repr(node.value)
+ # escapes quotes in the string; ought to be the same in Javascript
+ return node.value
+
+ def format(self, node, doc):
+ if doc is None:
+ return ''
+
+ cleaned_up_gtkdoc = super(DevDocsFormatterGjs, self).format_inline(node, doc)
+ return markdown.markdown(cleaned_up_gtkdoc, extensions=[
+ 'markdown.extensions.fenced_code',
+ 'markdown.extensions.nl2br',
+ 'markdown.extensions.attr_list',
+ HeaderIdExtension(forceid=False)
+ ])
+
+ def format_function_name(self, func):
+ name = func.name
+ if func.shadows:
+ name = func.shadows
+
+ if isinstance(func, ast.VFunction):
+ return 'vfunc_' + name
+ return name
+
+ def format_page_name(self, node):
+ if isinstance(node, ast.Function) and node.parent is not None:
+ return node.parent.name + "." + self.format_function_name(node)
+ return super(DevDocsFormatterGjs, self).format_page_name(node)
+
+ def _write_xref_markdown(self, target, anchor=None, display_name=None, pluralize=False):
+ if display_name is None:
+ display_name = target
+ link = target + ".html"
+ if anchor is not None:
+ link += "#" + anchor
+ return "[{}]({}){}".format(display_name, link, 's' if pluralize else '')
+
+ def to_underscores(self, node):
+ try:
+ return super(DevDocsFormatterGjs, self).to_underscores(node)
+ except Exception as e:
+ if e.message == 'invalid node':
+ print('warning: invalid node in', node.parent.name,
+ file=sys.stderr)
+ return node.parent.name + '_invalid_node'
+
+ def make_anchor(self, node):
+ style_class = get_node_kind(node)
+ return "{}-{}".format(style_class, self.to_underscores(node))
+
+ def _process_parameter(self, node, match, props):
+ # Display the instance parameter as "this" instead of whatever name it
+ # has in C.
+ if hasattr(node, 'instance_parameter') and \
+ node.instance_parameter is not None and \
+ props['param_name'] == node.instance_parameter.argname:
+ return '<code>this</code>'
+ return super(DevDocsFormatterGjs, self)._process_parameter(node, match, props)
+
+ def format_xref(self, node, pluralize=False, **attrdict):
+ if node is None or not hasattr(node, 'namespace'):
+ return self._write_xref_markdown('index')
+ if node.namespace is self._transformer.namespace:
+ return self.format_internal_xref(node, attrdict, pluralize=pluralize)
+ return self.format_external_xref(node, attrdict, pluralize=pluralize)
+
+ def format_internal_xref(self, node, attrdict, pluralize=False):
+ if not self.should_render_node(node):
+ # Non-toplevel nodes are linked to the main page.
+ page = make_page_id(node.parent)
+ name = node.name
+ if isinstance(node, ast.Member):
+ name = name.upper()
+ return self._write_xref_markdown(page, self.make_anchor(node),
+ page + "." + name,
+ pluralize=pluralize)
+ return self._write_xref_markdown(make_page_id(node), pluralize=pluralize)
+
+ def format_external_xref(self, node, attrdict, pluralize=False):
+ ns = node.namespace
+ slug = ns.name.lower() + str(ns.version).replace('.', '')
+ if not self.should_render_node(node):
+ target = 'gir:///%s/%s' % (slug, make_page_id(node.parent))
+ return self._write_xref_markdown(target, self.make_anchor(node),
+ self.format_page_name(node.parent),
+ pluralize=pluralize)
+ target = 'gir:///%s/%s' % (slug, make_page_id(node))
+ return self._write_xref_markdown(target, None,
+ self.format_page_name(node),
+ pluralize=pluralize)
+
+ def format_inline(self, node, para):
+ if para is None:
+ return ''
+ cleaned_up_gtkdoc = super(DevDocsFormatterGjs, self).format_inline(node, para)
+ return markdown.markdown(cleaned_up_gtkdoc, extensions=[
+ InlineMarkdown(),
+ 'markdown.extensions.fenced_code',
+ 'markdown.extensions.nl2br',
+ 'markdown.extensions.attr_list',
+ HeaderIdExtension(forceid=False)
+ ])
+
+ def format_in_parameters(self, node):
+ return ', '.join(p.argname for p in self.get_in_parameters(node))
+
LANGUAGES = {
+ "devdocs": {
+ "gjs": DevDocsFormatterGjs,
+ },
"mallard": {
"c": DocFormatterC,
"python": DocFormatterPython,
@@ -910,6 +1290,7 @@ class DocWriter(object):
self._formatter = formatter_class(self._transformer)
self._language = self._formatter.language
+ self._output_format = output_format
self._lookup = self._get_template_lookup()
@@ -968,6 +1349,7 @@ class DocWriter(object):
node=node,
page_id=page_id,
page_kind=page_kind,
+ get_node_kind=get_node_kind,
formatter=self._formatter,
ast=ast)
diff --git a/giscanner/mdextensions.py b/giscanner/mdextensions.py
new file mode 100644
index 00000000..16af4fc3
--- /dev/null
+++ b/giscanner/mdextensions.py
@@ -0,0 +1,13 @@
+from markdown.extensions import Extension
+from markdown.treeprocessors import Treeprocessor
+
+
+class RemoveOuterP(Treeprocessor):
+ def run(self, root):
+ if len(root) == 1 and root[0].tag == "p":
+ root[0].tag = "span"
+
+
+class InlineMarkdown(Extension):
+ def extendMarkdown(self, md, md_globals):
+ md.treeprocessors.add("remove_outer_p", RemoveOuterP(md), "_end")
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 1a27aa3c..c3187e82 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -262,6 +262,7 @@ currently-scanned namespace is first."""
stderr=subprocess.PIPE)
_name = name
proc_name, err = proc.communicate(name.encode())
+ proc_name = proc_name.strip()
if proc.returncode:
raise ValueError('filter: %r exited: %d with error: %s' %
(self._symbol_filter_cmd, proc.returncode, err))