(root)/
glib-2.79.0/
gio/
gdbus-2.0/
codegen/
codegen_rst.py
       1  # SPDX-FileCopyrightText: 2022 Emmanuele Bassi
       2  #
       3  # SPDX-License-Identifier: LGPL-2.1-or-later
       4  
       5  import os
       6  import re
       7  
       8  from . import utils
       9  import textwrap
      10  
      11  # Disable line length warnings as wrapping the templates would be hard
      12  # flake8: noqa: E501
      13  
      14  
      15  class ESC[4;38;5;81mRstCodeGenerator:
      16      """Generates documentation in reStructuredText format."""
      17  
      18      def __init__(self, ifaces):
      19          self.ifaces = ifaces
      20          self._generate_expand_dicts()
      21  
      22      def _expand(self, s, expandParamsAndConstants):
      23          """Expands parameters and constant literals."""
      24          res = []
      25          for line in textwrap.dedent(s).split("\n"):
      26              line = line.rstrip()
      27              if line == "":
      28                  res.append("")
      29                  continue
      30              for key in self._expand_member_dict_keys:
      31                  line = line.replace(key, self._expand_member_dict[key])
      32              for key in self._expand_iface_dict_keys:
      33                  line = line.replace(key, self._expand_iface_dict[key])
      34              if expandParamsAndConstants:
      35                  # replace @foo with ``foo``
      36                  line = re.sub(
      37                      "@[a-zA-Z0-9_]*",
      38                      lambda m: "``" + m.group(0)[1:] + "``",
      39                      line,
      40                  )
      41                  # replace e.g. %TRUE with ``TRUE``
      42                  line = re.sub(
      43                      "%[a-zA-Z0-9_]*",
      44                      lambda m: "``" + m.group(0)[1:] + "``",
      45                      line,
      46                  )
      47              res.append(line)
      48          return "\n".join(res)
      49  
      50      def _generate_expand_dicts(self):
      51          """Generates the dictionaries used to expand gtk-doc sigils."""
      52          self._expand_member_dict = {}
      53          self._expand_iface_dict = {}
      54          for i in self.ifaces:
      55              key = f"#{i.name}"
      56              value = f"`{i.name}`_"
      57              self._expand_iface_dict[key] = value
      58  
      59              for m in i.methods:
      60                  key = "%s.%s()" % (i.name, m.name)
      61                  value = f"`{i.name}.{m.name}`_"
      62                  self._expand_member_dict[key] = value
      63  
      64              for s in i.signals:
      65                  key = "#%s::%s" % (i.name, s.name)
      66                  value = f"`{i.name}::{s.name}`_"
      67                  self._expand_member_dict[key] = value
      68  
      69              for p in i.properties:
      70                  key = "#%s:%s" % (i.name, p.name)
      71                  value = f"`{i.name}:{p.name}`_"
      72                  self._expand_member_dict[key] = value
      73  
      74          # Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
      75          # is evaluated before #org.foo.Iface:Media ...
      76          self._expand_member_dict_keys = sorted(
      77              self._expand_member_dict.keys(), reverse=True
      78          )
      79          self._expand_iface_dict_keys = sorted(
      80              self._expand_iface_dict.keys(), reverse=True
      81          )
      82  
      83      def _generate_header(self, iface):
      84          """Generates the header and preamble of the document."""
      85          header_len = len(iface.name)
      86          res = [
      87              f".. _{iface.name}:",
      88              "",
      89              "=" * header_len,
      90              iface.name,
      91              "=" * header_len,
      92              "",
      93              "-----------",
      94              "Description",
      95              "-----------",
      96              "",
      97              f".. _{iface.name} Description:",
      98              "",
      99              iface.doc_string_brief.strip(),
     100              "",
     101              self._expand(iface.doc_string, True),
     102              "",
     103          ]
     104          if iface.since:
     105              res += [
     106                  f"Interface available since: {iface.since}.",
     107                  "",
     108              ]
     109          if iface.deprecated:
     110              res += [
     111                  ".. warning::",
     112                  "",
     113                  "   This interface is deprecated.",
     114                  "",
     115                  "",
     116              ]
     117          res += [""]
     118          return "\n".join(res)
     119  
     120      def _generate_section(self, title, name):
     121          """Generates a section with the given title."""
     122          res = [
     123              f".. _{name} {title}:",
     124              "",
     125              "-" * len(title),
     126              title,
     127              "-" * len(title),
     128              "",
     129              "",
     130          ]
     131          return "\n".join(res)
     132  
     133      def _generate_properties(self, iface):
     134          """Generates the properties section."""
     135          res = []
     136          for p in iface.properties:
     137              title = f"{iface.name}:{p.name}"
     138              if p.readable and p.writable:
     139                  access = "readwrite"
     140              elif p.writable:
     141                  access = "writable"
     142              else:
     143                  access = "readable"
     144              res += [
     145                  f".. _{title}:",
     146                  "",
     147                  title,
     148                  "^" * len(title),
     149                  "",
     150                  "::",
     151                  "",
     152                  f"    {p.name} {access} {p.signature}",
     153                  "",
     154                  "",
     155                  self._expand(p.doc_string, True),
     156                  "",
     157              ]
     158              if p.since:
     159                  res += [
     160                      f"Property available since: {p.since}.",
     161                      "",
     162                  ]
     163              if p.deprecated:
     164                  res += [
     165                      ".. warning::",
     166                      "",
     167                      "   This property is deprecated.",
     168                      "",
     169                      "",
     170                  ]
     171              res += [""]
     172          return "\n".join(res)
     173  
     174      def _generate_method_signature(self, method):
     175          """Generates the method signature as a code block."""
     176          res = [
     177              "::",
     178              "",
     179          ]
     180          n_in_args = len(method.in_args)
     181          n_out_args = len(method.out_args)
     182          if n_in_args == 0 and n_out_args == 0:
     183              res += [
     184                  f"    {method.name} ()",
     185              ]
     186          else:
     187              res += [
     188                  f"    {method.name} (",
     189              ]
     190              for idx, arg in enumerate(method.in_args):
     191                  if idx == n_in_args - 1 and n_out_args == 0:
     192                      res += [
     193                          f"      IN {arg.name} {arg.signature}",
     194                      ]
     195                  else:
     196                      res += [
     197                          f"      IN {arg.name} {arg.signature},",
     198                      ]
     199              for idx, arg in enumerate(method.out_args):
     200                  if idx == n_out_args - 1:
     201                      res += [
     202                          f"      OUT {arg.name} {arg.signature}",
     203                      ]
     204                  else:
     205                      res += [
     206                          f"      OUT {arg.name} {arg.signature},",
     207                      ]
     208              res += [
     209                  "    )",
     210                  "",
     211              ]
     212          res += [""]
     213          return "\n".join(res)
     214  
     215      def _generate_methods(self, iface):
     216          """Generates the methods section."""
     217          res = []
     218          for m in iface.methods:
     219              title = f"{iface.name}.{m.name}"
     220              res += [
     221                  f".. _{title}:",
     222                  "",
     223                  title,
     224                  "^" * len(title),
     225                  "",
     226                  self._generate_method_signature(m),
     227                  "",
     228                  self._expand(m.doc_string, True),
     229                  "",
     230              ]
     231              for a in m.in_args:
     232                  arg_desc = self._expand(a.doc_string, True)
     233                  res += [
     234                      f"{a.name}",
     235                      f"  {arg_desc}",
     236                      "",
     237                  ]
     238              for a in m.out_args:
     239                  arg_desc = self._expand(a.doc_string, True)
     240                  res += [
     241                      f"{a.name}",
     242                      f"  {arg_desc}",
     243                      "",
     244                  ]
     245              res += [""]
     246              if m.since:
     247                  res += [
     248                      f"Method available since: {m.since}.",
     249                      "",
     250                  ]
     251              if m.deprecated:
     252                  res += [
     253                      ".. warning::",
     254                      "",
     255                      "   This method is deprecated.",
     256                      "",
     257                      "",
     258                  ]
     259              res += [""]
     260          return "\n".join(res)
     261  
     262      def _generate_signal_signature(self, signal):
     263          """Generates the signal signature."""
     264          res = [
     265              "::",
     266              "",
     267          ]
     268          n_args = len(signal.args)
     269          if n_args == 0:
     270              res += [
     271                  f"    {signal.name} ()",
     272              ]
     273          else:
     274              res += [
     275                  f"    {signal.name} (",
     276              ]
     277              for idx, arg in enumerate(signal.args):
     278                  if idx == n_args - 1:
     279                      res += [
     280                          f"      {arg.name} {arg.signature}",
     281                      ]
     282                  else:
     283                      res += [
     284                          f"      {arg.name} {arg.signature},",
     285                      ]
     286              res += [
     287                  "    )",
     288                  "",
     289              ]
     290          res += [""]
     291          return "\n".join(res)
     292  
     293      def _generate_signals(self, iface):
     294          """Generates the signals section."""
     295          res = []
     296          for s in iface.signals:
     297              title = f"{iface.name}::{s.name}"
     298              res += [
     299                  f".. _{title}:",
     300                  "",
     301                  title,
     302                  "^" * len(title),
     303                  "",
     304                  self._generate_signal_signature(s),
     305                  "",
     306                  self._expand(s.doc_string, True),
     307                  "",
     308              ]
     309              for a in s.args:
     310                  arg_desc = self._expand(a.doc_string, True)
     311                  res += [
     312                      f"{a.name}",
     313                      f"  {arg_desc}",
     314                      "",
     315                  ]
     316              res += [""]
     317              if s.since:
     318                  res += [
     319                      f"Signal available since: {s.since}.",
     320                      "",
     321                  ]
     322              if s.deprecated:
     323                  res += [
     324                      ".. warning::",
     325                      "",
     326                      "   This signal is deprecated.",
     327                      "",
     328                      "",
     329                  ]
     330              res += [""]
     331          return "\n".join(res)
     332  
     333      def generate(self, rst, outdir):
     334          """Generates the reStructuredText file for each interface."""
     335          for i in self.ifaces:
     336              with open(os.path.join(outdir, f"{rst}-{i.name}.rst"), "w") as outfile:
     337                  outfile.write(self._generate_header(i))
     338                  if len(i.properties) > 0:
     339                      outfile.write(self._generate_section("Properties", i.name))
     340                      outfile.write(self._generate_properties(i))
     341                  if len(i.methods) > 0:
     342                      outfile.write(self._generate_section("Methods", i.name))
     343                      outfile.write(self._generate_methods(i))
     344                  if len(i.signals) > 0:
     345                      outfile.write(self._generate_section("Signals", i.name))
     346                      outfile.write(self._generate_signals(i))