mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
2b2b04d188
The title of an interface can be arbitrarily long, considering that reverse DNS namespaces can be pretty complex. Instead of using the whole interface name, we can use the name without the prefix.
348 lines
11 KiB
Python
348 lines
11 KiB
Python
# SPDX-FileCopyrightText: 2022 Emmanuele Bassi
|
|
#
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
import os
|
|
import re
|
|
|
|
from . import utils
|
|
import textwrap
|
|
|
|
# Disable line length warnings as wrapping the templates would be hard
|
|
# flake8: noqa: E501
|
|
|
|
|
|
class RstCodeGenerator:
|
|
"""Generates documentation in reStructuredText 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 textwrap.dedent(s).split("\n"):
|
|
line = line.rstrip()
|
|
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."""
|
|
iface_name = iface.name_without_prefix
|
|
header_len = len(iface_name)
|
|
res = [
|
|
f".. _{iface.name}:",
|
|
"",
|
|
"=" * header_len,
|
|
iface_name,
|
|
"=" * header_len,
|
|
"",
|
|
"-----------",
|
|
"Description",
|
|
"-----------",
|
|
"",
|
|
f".. _{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 = [
|
|
f".. _{name} {title}:",
|
|
"",
|
|
"-" * len(title),
|
|
title,
|
|
"-" * len(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 += [
|
|
f".. _{title}:",
|
|
"",
|
|
title,
|
|
"^" * len(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 += [
|
|
f".. _{title}:",
|
|
"",
|
|
title,
|
|
"^" * len(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}",
|
|
f" {arg_desc}",
|
|
"",
|
|
]
|
|
for a in m.out_args:
|
|
arg_desc = self._expand(a.doc_string, True)
|
|
res += [
|
|
f"{a.name}",
|
|
f" {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 += [
|
|
f".. _{title}:",
|
|
"",
|
|
title,
|
|
"^" * len(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, rst, outdir):
|
|
"""Generates the reStructuredText file for each interface."""
|
|
for i in self.ifaces:
|
|
with open(os.path.join(outdir, f"{rst}-{i.name}.rst"), "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))
|