mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
codegen: Support markdown suitable for gi-docgen
This commit is contained in:
parent
f0bc060e89
commit
4cb945d780
@ -30,6 +30,7 @@ from . import dbustypes
|
||||
from . import parser
|
||||
from . import codegen
|
||||
from . import codegen_docbook
|
||||
from . import codegen_md
|
||||
from . import codegen_rst
|
||||
from .utils import print_error, print_warning
|
||||
|
||||
@ -212,6 +213,11 @@ def codegen_main():
|
||||
metavar="OUTFILES",
|
||||
help="Generate Docbook in OUTFILES-org.Project.IFace.xml",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--generate-md",
|
||||
metavar="OUTFILES",
|
||||
help="Generate Markdown in OUTFILES-org.Project.IFace.md",
|
||||
)
|
||||
arg_parser.add_argument(
|
||||
"--generate-rst",
|
||||
metavar="OUTFILES",
|
||||
@ -295,10 +301,11 @@ def codegen_main():
|
||||
if (
|
||||
args.generate_c_code is not None
|
||||
or args.generate_docbook is not None
|
||||
or args.generate_md is not None
|
||||
or args.generate_rst is not None
|
||||
) and args.output is not None:
|
||||
print_error(
|
||||
"Using --generate-c-code or --generate-docbook or --generate-rst and "
|
||||
"Using --generate-c-code or --generate-{docbook,md,rst} and "
|
||||
"--output at the same time is not allowed"
|
||||
)
|
||||
|
||||
@ -428,6 +435,11 @@ def codegen_main():
|
||||
if docbook:
|
||||
docbook_gen.generate(docbook, args.output_directory)
|
||||
|
||||
md = args.generate_md
|
||||
md_gen = codegen_md.MdCodeGenerator(all_ifaces)
|
||||
if md:
|
||||
md_gen.generate(md, args.output_directory)
|
||||
|
||||
rst = args.generate_rst
|
||||
rst_gen = codegen_rst.RstCodeGenerator(all_ifaces)
|
||||
if rst:
|
||||
|
302
gio/gdbus-2.0/codegen/codegen_md.py
Normal file
302
gio/gdbus-2.0/codegen/codegen_md.py
Normal file
@ -0,0 +1,302 @@
|
||||
# SPDX-FileCopyrightText: 2023 Guido Günther
|
||||
# base on # codegen_rst.py (C) 2022 Emmanuele Bassi
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from . import utils
|
||||
|
||||
# Disable line length warnings as wrapping the templates would be hard
|
||||
# flake8: noqa: E501
|
||||
|
||||
|
||||
class MdCodeGenerator:
|
||||
"""Generates documentation in Markdown format."""
|
||||
|
||||
def __init__(self, ifaces):
|
||||
self.ifaces = ifaces
|
||||
self._generate_expand_dicts()
|
||||
|
||||
def _expand(self, s, expandParamsAndConstants):
|
||||
"""Expands parameters and constant literals."""
|
||||
res = []
|
||||
for line in s.split("\n"):
|
||||
line = line.strip()
|
||||
if line == "":
|
||||
res.append("")
|
||||
continue
|
||||
for key in self._expand_member_dict_keys:
|
||||
line = line.replace(key, self._expand_member_dict[key])
|
||||
for key in self._expand_iface_dict_keys:
|
||||
line = line.replace(key, self._expand_iface_dict[key])
|
||||
if expandParamsAndConstants:
|
||||
# replace @foo with `foo`
|
||||
line = re.sub(
|
||||
"@[a-zA-Z0-9_]*",
|
||||
lambda m: "`" + m.group(0)[1:] + "`",
|
||||
line,
|
||||
)
|
||||
# replace e.g. %TRUE with ``TRUE``
|
||||
line = re.sub(
|
||||
"%[a-zA-Z0-9_]*",
|
||||
lambda m: "`" + m.group(0)[1:] + "`",
|
||||
line,
|
||||
)
|
||||
res.append(line)
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_expand_dicts(self):
|
||||
"""Generates the dictionaries used to expand gtk-doc sigils."""
|
||||
self._expand_member_dict = {}
|
||||
self._expand_iface_dict = {}
|
||||
for i in self.ifaces:
|
||||
key = f"#{i.name}"
|
||||
value = f"`{i.name}`_"
|
||||
self._expand_iface_dict[key] = value
|
||||
|
||||
for m in i.methods:
|
||||
key = "%s.%s()" % (i.name, m.name)
|
||||
value = f"`{i.name}.{m.name}`_"
|
||||
self._expand_member_dict[key] = value
|
||||
|
||||
for s in i.signals:
|
||||
key = "#%s::%s" % (i.name, s.name)
|
||||
value = f"`{i.name}::{s.name}`_"
|
||||
self._expand_member_dict[key] = value
|
||||
|
||||
for p in i.properties:
|
||||
key = "#%s:%s" % (i.name, p.name)
|
||||
value = f"`{i.name}:{p.name}`_"
|
||||
self._expand_member_dict[key] = value
|
||||
|
||||
# Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
|
||||
# is evaluated before #org.foo.Iface:Media ...
|
||||
self._expand_member_dict_keys = sorted(
|
||||
self._expand_member_dict.keys(), reverse=True
|
||||
)
|
||||
self._expand_iface_dict_keys = sorted(
|
||||
self._expand_iface_dict.keys(), reverse=True
|
||||
)
|
||||
|
||||
def _generate_header(self, iface):
|
||||
"""Generates the header and preamble of the document."""
|
||||
header_len = len(iface.name)
|
||||
res = [
|
||||
f"Title: {iface.name} D-Bus Interface",
|
||||
f"Slug: {iface.name}",
|
||||
"",
|
||||
"# " + iface.name,
|
||||
"",
|
||||
"## Description",
|
||||
"",
|
||||
iface.doc_string_brief.strip(),
|
||||
"",
|
||||
self._expand(iface.doc_string, True),
|
||||
"",
|
||||
]
|
||||
if iface.since:
|
||||
res += [
|
||||
f"Interface available since: {iface.since}.",
|
||||
"",
|
||||
]
|
||||
if iface.deprecated:
|
||||
res += [
|
||||
"*Warning*: This interface is deprecated.",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_section(self, title, name):
|
||||
"""Generates a section with the given title."""
|
||||
res = [
|
||||
"### " + title,
|
||||
"",
|
||||
]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_properties(self, iface):
|
||||
"""Generates the properties section."""
|
||||
res = []
|
||||
for p in iface.properties:
|
||||
title = f"{iface.name}:{p.name}"
|
||||
if p.readable and p.writable:
|
||||
access = "readwrite"
|
||||
elif p.writable:
|
||||
access = "writable"
|
||||
else:
|
||||
access = "readable"
|
||||
res += [
|
||||
"### " + title,
|
||||
"",
|
||||
"```",
|
||||
f" {p.name} {access} {p.signature}",
|
||||
"```",
|
||||
"",
|
||||
self._expand(p.doc_string, True),
|
||||
"",
|
||||
]
|
||||
if p.since:
|
||||
res += [
|
||||
f"Property available since: {p.since}.",
|
||||
"",
|
||||
]
|
||||
if p.deprecated:
|
||||
res += [
|
||||
"*Warning*: This property is deprecated.",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_method_signature(self, method):
|
||||
"""Generates the method signature as a code block."""
|
||||
res = [
|
||||
"```",
|
||||
]
|
||||
n_in_args = len(method.in_args)
|
||||
n_out_args = len(method.out_args)
|
||||
if n_in_args == 0 and n_out_args == 0:
|
||||
res += [
|
||||
f" {method.name} ()",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" {method.name} (",
|
||||
]
|
||||
for idx, arg in enumerate(method.in_args):
|
||||
if idx == n_in_args - 1 and n_out_args == 0:
|
||||
res += [
|
||||
f" IN {arg.name} {arg.signature}",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" IN {arg.name} {arg.signature},",
|
||||
]
|
||||
for idx, arg in enumerate(method.out_args):
|
||||
if idx == n_out_args - 1:
|
||||
res += [
|
||||
f" OUT {arg.name} {arg.signature}",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" OUT {arg.name} {arg.signature},",
|
||||
]
|
||||
res += [
|
||||
" )",
|
||||
]
|
||||
res += ["```"]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_methods(self, iface):
|
||||
"""Generates the methods section."""
|
||||
res = []
|
||||
for m in iface.methods:
|
||||
title = f"{iface.name}.{m.name}"
|
||||
res += [
|
||||
"### " + title,
|
||||
"",
|
||||
self._generate_method_signature(m),
|
||||
"",
|
||||
self._expand(m.doc_string, True),
|
||||
"",
|
||||
]
|
||||
for a in m.in_args:
|
||||
arg_desc = self._expand(a.doc_string, True)
|
||||
res += [
|
||||
f"* {a.name}: {arg_desc}",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
if m.since:
|
||||
res += [
|
||||
f"Method available since: {m.since}.",
|
||||
"",
|
||||
]
|
||||
if m.deprecated:
|
||||
res += [
|
||||
"*Warning*: This method is deprecated.",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_signal_signature(self, signal):
|
||||
"""Generates the signal signature."""
|
||||
res = [
|
||||
"```",
|
||||
]
|
||||
n_args = len(signal.args)
|
||||
if n_args == 0:
|
||||
res += [
|
||||
f" {signal.name} ()",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" {signal.name} (",
|
||||
]
|
||||
for idx, arg in enumerate(signal.args):
|
||||
if idx == n_args - 1:
|
||||
res += [
|
||||
f" {arg.name} {arg.signature}",
|
||||
]
|
||||
else:
|
||||
res += [
|
||||
f" {arg.name} {arg.signature},",
|
||||
]
|
||||
res += [
|
||||
" )",
|
||||
]
|
||||
res += ["```"]
|
||||
return "\n".join(res)
|
||||
|
||||
def _generate_signals(self, iface):
|
||||
"""Generates the signals section."""
|
||||
res = []
|
||||
for s in iface.signals:
|
||||
title = f"{iface.name}::{s.name}"
|
||||
res += [
|
||||
"### " + title,
|
||||
"",
|
||||
self._generate_signal_signature(s),
|
||||
"",
|
||||
self._expand(s.doc_string, True),
|
||||
"",
|
||||
]
|
||||
for a in s.args:
|
||||
arg_desc = self._expand(a.doc_string, True)
|
||||
res += [
|
||||
f"{a.name}",
|
||||
f" {arg_desc}",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
if s.since:
|
||||
res += [
|
||||
f"Signal available since: {s.since}.",
|
||||
"",
|
||||
]
|
||||
if s.deprecated:
|
||||
res += [
|
||||
"*Warning*: This signal is deprecated.",
|
||||
"",
|
||||
]
|
||||
res += [""]
|
||||
return "\n".join(res)
|
||||
|
||||
def generate(self, md, outdir):
|
||||
"""Generates the Markdown file for each interface."""
|
||||
for i in self.ifaces:
|
||||
with open(os.path.join(outdir, f"{md}-{i.name}.md"), "w") as outfile:
|
||||
outfile.write(self._generate_header(i))
|
||||
if len(i.properties) > 0:
|
||||
outfile.write(self._generate_section("Properties", i.name))
|
||||
outfile.write(self._generate_properties(i))
|
||||
if len(i.methods) > 0:
|
||||
outfile.write(self._generate_section("Methods", i.name))
|
||||
outfile.write(self._generate_methods(i))
|
||||
if len(i.signals) > 0:
|
||||
outfile.write(self._generate_section("Signals", i.name))
|
||||
outfile.write(self._generate_signals(i))
|
@ -3,6 +3,7 @@ gdbus_codegen_files = [
|
||||
'codegen.py',
|
||||
'codegen_main.py',
|
||||
'codegen_docbook.py',
|
||||
'codegen_md.py',
|
||||
'codegen_rst.py',
|
||||
'dbustypes.py',
|
||||
'parser.py',
|
||||
|
@ -411,6 +411,26 @@ G_END_DECLS
|
||||
xml_data = f.readlines()
|
||||
self.assertTrue(len(xml_data) != 0)
|
||||
|
||||
def test_generate_md(self):
|
||||
"""Test the basic functionality of the markdown generator."""
|
||||
xml_contents = """
|
||||
<node>
|
||||
<interface name="org.project.Bar.Frobnicator">
|
||||
<method name="RandomMethod"/>
|
||||
</interface>
|
||||
</node>
|
||||
"""
|
||||
res = self.runCodegenWithInterface(
|
||||
xml_contents,
|
||||
"--generate-md",
|
||||
"test",
|
||||
)
|
||||
self.assertEqual("", res.err)
|
||||
self.assertEqual("", res.out)
|
||||
with open("test-org.project.Bar.Frobnicator.md", "r") as f:
|
||||
rst = f.readlines()
|
||||
self.assertTrue(len(rst) != 0)
|
||||
|
||||
def test_generate_rst(self):
|
||||
"""Test the basic functionality of the rst generator."""
|
||||
xml_contents = """
|
||||
|
Loading…
Reference in New Issue
Block a user