glib (2.79.0)

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