[PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections

John Snow posted 5 patches 5 months, 4 weeks ago
Maintainers: Markus Armbruster <armbru@redhat.com>, Michael Roth <michael.roth@amd.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, "Daniel P. Berrangé" <berrange@redhat.com>, Kashyap Chamarthy <kchamart@redhat.com>, Kostiantyn Kostiuk <kkostiuk@redhat.com>, Kevin Wolf <kwolf@redhat.com>, "Michael S. Tsirkin" <mst@redhat.com>, Stefano Garzarella <sgarzare@redhat.com>, John Snow <jsnow@redhat.com>, Peter Maydell <peter.maydell@linaro.org>, Cleber Rosa <crosa@redhat.com>, Eric Blake <eblake@redhat.com>, Igor Mammedov <imammedo@redhat.com>, Ani Sinha <anisinha@redhat.com>, Gerd Hoffmann <kraxel@redhat.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, Hanna Reitz <hreitz@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, "Gonglei (Arei)" <arei.gonglei@huawei.com>, Zhenwei Pi <pizhenwei@bytedance.com>, Jonathan Cameron <jonathan.cameron@huawei.com>, Fan Ni <fan.ni@samsung.com>, Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>, Eduardo Habkost <eduardo@habkost.net>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, Yanan Wang <wangyanan55@huawei.com>, Zhao Liu <zhao1.liu@intel.com>, Peter Xu <peterx@redhat.com>, Fabiano Rosas <farosas@suse.de>, Jason Wang <jasowang@redhat.com>, "Alex Bennée" <alex.bennee@linaro.org>, Jiri Pirko <jiri@resnulli.us>, Stefan Berger <stefanb@linux.vnet.ibm.com>, Stefan Hajnoczi <stefanha@redhat.com>, Mads Ynddal <mads@ynddal.dk>, Alex Williamson <alex.williamson@redhat.com>, "Cédric Le Goater" <clg@redhat.com>, Lukas Straub <lukasstraub2@web.de>
[PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections
Posted by John Snow 5 months, 4 weeks ago
This change removes special parsing for freeform sections and allows
them to simply be unmodified rST syntax. The existing headings in the
QAPI schema are adjusted to reflect the new paradigm.

Tests and documentation are updated to match.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 docs/devel/qapi-code-gen.rst         | 28 ++++++++++++++-------
 docs/interop/firmware.json           |  4 ++-
 docs/interop/vhost-user.json         |  4 ++-
 docs/sphinx/qapidoc.py               | 37 +---------------------------
 qapi/acpi.json                       |  4 ++-
 qapi/audio.json                      |  4 ++-
 qapi/authz.json                      |  4 ++-
 qapi/block-core.json                 |  3 ++-
 qapi/block-export.json               |  3 ++-
 qapi/block.json                      |  7 ++++--
 qapi/char.json                       |  4 ++-
 qapi/common.json                     |  4 ++-
 qapi/compat.json                     |  4 ++-
 qapi/control.json                    |  4 ++-
 qapi/crypto.json                     |  4 ++-
 qapi/cryptodev.json                  |  4 ++-
 qapi/cxl.json                        |  4 ++-
 qapi/dump.json                       |  4 ++-
 qapi/ebpf.json                       |  4 ++-
 qapi/error.json                      |  4 ++-
 qapi/introspect.json                 |  4 ++-
 qapi/job.json                        |  4 ++-
 qapi/machine-common.json             |  4 ++-
 qapi/machine.json                    |  4 ++-
 qapi/migration.json                  |  4 ++-
 qapi/misc.json                       |  4 ++-
 qapi/net.json                        |  4 ++-
 qapi/pci.json                        |  4 ++-
 qapi/qapi-schema.json                |  4 ++-
 qapi/qdev.json                       |  4 ++-
 qapi/qom.json                        |  4 ++-
 qapi/replay.json                     |  4 ++-
 qapi/rocker.json                     |  4 ++-
 qapi/run-state.json                  |  4 ++-
 qapi/sockets.json                    |  4 ++-
 qapi/stats.json                      |  4 ++-
 qapi/tpm.json                        |  4 ++-
 qapi/trace.json                      |  4 ++-
 qapi/transaction.json                |  4 ++-
 qapi/uefi.json                       |  4 ++-
 qapi/ui.json                         | 14 ++++++++---
 qapi/vfio.json                       |  4 ++-
 qapi/virtio.json                     |  4 ++-
 qapi/yank.json                       |  4 ++-
 scripts/qapi/parser.py               |  7 ------
 storage-daemon/qapi/qapi-schema.json |  8 ++++--
 tests/qapi-schema/doc-good.json      | 10 +++++---
 tests/qapi-schema/doc-good.out       | 10 +++++---
 48 files changed, 173 insertions(+), 106 deletions(-)

diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
index 231cc0fecf7..dfdbeac5a5a 100644
--- a/docs/devel/qapi-code-gen.rst
+++ b/docs/devel/qapi-code-gen.rst
@@ -876,25 +876,35 @@ structuring content.
 Headings and subheadings
 ~~~~~~~~~~~~~~~~~~~~~~~~
 
-A free-form documentation comment containing a line which starts with
-some ``=`` symbols and then a space defines a section heading::
+Free-form documentation does not start with ``@SYMBOL`` and can contain
+arbitrary rST markup. Headings can be marked up using the standard rST
+syntax::
 
     ##
-    # = This is a top level heading
+    # *************************
+    # This is a level 2 heading
+    # *************************
     #
     # This is a free-form comment which will go under the
     # top level heading.
     ##
 
     ##
-    # == This is a second level heading
+    # This is a third level heading
+    # ==============================
+    #
+    # Level 4
+    # _______
+    #
+    # Level 5
+    # ^^^^^^^
+    #
+    # Level 6
+    # """""""
     ##
 
-A heading line must be the first line of the documentation
-comment block.
-
-Section headings must always be correctly nested, so you can only
-define a third-level heading inside a second-level heading, and so on.
+Level 1 headings are reserved for use by the generated documentation
+page itself, leaving level 2 as the highest level that should be used.
 
 
 Documentation markup
diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json
index 745d21d8223..f46fdedfa89 100644
--- a/docs/interop/firmware.json
+++ b/docs/interop/firmware.json
@@ -11,7 +11,9 @@
 # later. See the COPYING file in the top-level directory.
 
 ##
-# = Firmware
+# ********
+# Firmware
+# ********
 ##
 
 { 'pragma': {
diff --git a/docs/interop/vhost-user.json b/docs/interop/vhost-user.json
index b6ade9e4931..095eb99cf79 100644
--- a/docs/interop/vhost-user.json
+++ b/docs/interop/vhost-user.json
@@ -10,7 +10,9 @@
 # later. See the COPYING file in the top-level directory.
 
 ##
-# = vhost user backend discovery & capabilities
+# *******************************************
+# vhost user backend discovery & capabilities
+# *******************************************
 ##
 
 ##
diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
index d5d2b5eeb50..b794cde6971 100644
--- a/docs/sphinx/qapidoc.py
+++ b/docs/sphinx/qapidoc.py
@@ -399,44 +399,9 @@ def visit_module(self, path: str) -> None:
         self.ensure_blank_line()
 
     def visit_freeform(self, doc: QAPIDoc) -> None:
-        # TODO: Once the old qapidoc transformer is deprecated, freeform
-        # sections can be updated to pure rST, and this transformed removed.
-        #
-        # For now, translate our micro-format into rST. Code adapted
-        # from Peter Maydell's freeform().
-
         assert len(doc.all_sections) == 1, doc.all_sections
         body = doc.all_sections[0]
-        text = self.reformat_arobase(body.text)
-        info = doc.info
-
-        if re.match(r"=+ ", text):
-            # Section/subsection heading (if present, will always be the
-            # first line of the block)
-            (heading, _, text) = text.partition("\n")
-            (leader, _, heading) = heading.partition(" ")
-            # Implicit +1 for heading in the containing .rst doc
-            level = len(leader) + 1
-
-            # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections
-            markers = ' #*=_^"'
-            overline = level <= 2
-            marker = markers[level]
-
-            self.ensure_blank_line()
-            # This credits all 2 or 3 lines to the single source line.
-            if overline:
-                self.add_line(marker * len(heading), info)
-            self.add_line(heading, info)
-            self.add_line(marker * len(heading), info)
-            self.ensure_blank_line()
-
-            # Eat blank line(s) and advance info
-            trimmed = text.lstrip("\n")
-            text = trimmed
-            info = info.next_line(len(text) - len(trimmed) + 1)
-
-        self.add_lines(text, info)
+        self.add_lines(self.reformat_arobase(body.text), doc.info)
         self.ensure_blank_line()
 
     def visit_entity(self, ent: QAPISchemaDefinition) -> None:
diff --git a/qapi/acpi.json b/qapi/acpi.json
index 2d53b823656..90f8f564fda 100644
--- a/qapi/acpi.json
+++ b/qapi/acpi.json
@@ -6,7 +6,9 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
 ##
-# = ACPI
+# ****
+# ACPI
+# ****
 ##
 
 ##
diff --git a/qapi/audio.json b/qapi/audio.json
index 16de231f6d8..6a22ca384aa 100644
--- a/qapi/audio.json
+++ b/qapi/audio.json
@@ -7,7 +7,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Audio
+# *****
+# Audio
+# *****
 ##
 
 ##
diff --git a/qapi/authz.json b/qapi/authz.json
index 7fc6e3032ea..ad1b4b3af0c 100644
--- a/qapi/authz.json
+++ b/qapi/authz.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = User authorization
+# ******************
+# User authorization
+# ******************
 ##
 
 ##
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 1df6644f0de..8b413946ff8 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2,7 +2,8 @@
 # vim: filetype=python
 
 ##
-# == Block core (VM unrelated)
+# Block core (VM unrelated)
+# =========================
 ##
 
 { 'include': 'common.json' }
diff --git a/qapi/block-export.json b/qapi/block-export.json
index ed4deb54db2..2241bfecf25 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -2,7 +2,8 @@
 # vim: filetype=python
 
 ##
-# == Block device exports
+# Block device exports
+# ====================
 ##
 
 { 'include': 'sockets.json' }
diff --git a/qapi/block.json b/qapi/block.json
index 1490a1a17f8..2d54a81c366 100644
--- a/qapi/block.json
+++ b/qapi/block.json
@@ -2,13 +2,16 @@
 # vim: filetype=python
 
 ##
-# = Block devices
+# *************
+# Block devices
+# *************
 ##
 
 { 'include': 'block-core.json' }
 
 ##
-# == Additional block stuff (VM related)
+# Additional block stuff (VM related)
+# ===================================
 ##
 
 ##
diff --git a/qapi/char.json b/qapi/char.json
index df6e325e2e1..f38d04986dd 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Character devices
+# *****************
+# Character devices
+# *****************
 ##
 
 { 'include': 'sockets.json' }
diff --git a/qapi/common.json b/qapi/common.json
index 0e3a0bbbfb0..af7e3d618a7 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = Common data types
+# *****************
+# Common data types
+# *****************
 ##
 
 ##
diff --git a/qapi/compat.json b/qapi/compat.json
index 42034d9368c..90b8d51cf27 100644
--- a/qapi/compat.json
+++ b/qapi/compat.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = Compatibility policy
+# ********************
+# Compatibility policy
+# ********************
 ##
 
 ##
diff --git a/qapi/control.json b/qapi/control.json
index 34b733f63b6..ab0b3a3bbe5 100644
--- a/qapi/control.json
+++ b/qapi/control.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = QMP monitor control
+# *******************
+# QMP monitor control
+# *******************
 ##
 
 ##
diff --git a/qapi/crypto.json b/qapi/crypto.json
index 9ec6301e188..21006de36c4 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Cryptography
+# ************
+# Cryptography
+# ************
 ##
 
 ##
diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json
index b13db264034..1f49e1822c0 100644
--- a/qapi/cryptodev.json
+++ b/qapi/cryptodev.json
@@ -5,7 +5,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Cryptography devices
+# ********************
+# Cryptography devices
+# ********************
 ##
 
 ##
diff --git a/qapi/cxl.json b/qapi/cxl.json
index 8f2e9237b19..52cc5d4f336 100644
--- a/qapi/cxl.json
+++ b/qapi/cxl.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = CXL devices
+# ***********
+# CXL devices
+# ***********
 ##
 
 ##
diff --git a/qapi/dump.json b/qapi/dump.json
index d0ba1f0596f..0642ca157b8 100644
--- a/qapi/dump.json
+++ b/qapi/dump.json
@@ -5,7 +5,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Dump guest memory
+# *****************
+# Dump guest memory
+# *****************
 ##
 
 ##
diff --git a/qapi/ebpf.json b/qapi/ebpf.json
index db19ae850fc..d45054e6663 100644
--- a/qapi/ebpf.json
+++ b/qapi/ebpf.json
@@ -5,7 +5,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = eBPF Objects
+# ************
+# eBPF Objects
+# ************
 #
 # eBPF object is an ELF binary that contains the eBPF program and eBPF
 # map description(BTF).  Overall, eBPF object should contain the
diff --git a/qapi/error.json b/qapi/error.json
index 135c1e82319..54cb02fb880 100644
--- a/qapi/error.json
+++ b/qapi/error.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = QMP errors
+# **********
+# QMP errors
+# **********
 ##
 
 ##
diff --git a/qapi/introspect.json b/qapi/introspect.json
index e9e02972821..26d8839f19c 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -10,7 +10,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = QMP introspection
+# *****************
+# QMP introspection
+# *****************
 ##
 
 ##
diff --git a/qapi/job.json b/qapi/job.json
index 126fa5ce602..16b280f52f8 100644
--- a/qapi/job.json
+++ b/qapi/job.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = Background jobs
+# ***************
+# Background jobs
+# ***************
 ##
 
 ##
diff --git a/qapi/machine-common.json b/qapi/machine-common.json
index 298e51f373a..0f01599130c 100644
--- a/qapi/machine-common.json
+++ b/qapi/machine-common.json
@@ -5,7 +5,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Common machine types
+# ********************
+# Common machine types
+# ********************
 ##
 
 ##
diff --git a/qapi/machine.json b/qapi/machine.json
index 0650b8de71a..6ebb99dfabe 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -5,7 +5,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Machines
+# ********
+# Machines
+# ********
 ##
 
 { 'include': 'common.json' }
diff --git a/qapi/migration.json b/qapi/migration.json
index 4963f6ca127..84f4c800f7b 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Migration
+# *********
+# Migration
+# *********
 ##
 
 { 'include': 'common.json' }
diff --git a/qapi/misc.json b/qapi/misc.json
index 4b9e601cfa5..a180c16db25 100644
--- a/qapi/misc.json
+++ b/qapi/misc.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Miscellanea
+# ***********
+# Miscellanea
+# ***********
 ##
 
 { 'include': 'common.json' }
diff --git a/qapi/net.json b/qapi/net.json
index 97ea1839813..3b03843c165 100644
--- a/qapi/net.json
+++ b/qapi/net.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Net devices
+# ***********
+# Net devices
+# ***********
 ##
 
 { 'include': 'sockets.json' }
diff --git a/qapi/pci.json b/qapi/pci.json
index dc85a41d28b..a8671cd9ac3 100644
--- a/qapi/pci.json
+++ b/qapi/pci.json
@@ -6,7 +6,9 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
 ##
-# = PCI
+# ***
+# PCI
+# ***
 ##
 
 ##
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
index a8f66163cb7..49b9a0267d3 100644
--- a/qapi/qapi-schema.json
+++ b/qapi/qapi-schema.json
@@ -1,7 +1,9 @@
 # -*- Mode: Python -*-
 # vim: filetype=python
 ##
-# = Introduction
+# ************
+# Introduction
+# ************
 #
 # This manual describes the commands and events supported by the QEMU
 # Monitor Protocol (QMP).
diff --git a/qapi/qdev.json b/qapi/qdev.json
index 32c7d100463..441ed518b87 100644
--- a/qapi/qdev.json
+++ b/qapi/qdev.json
@@ -5,7 +5,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Device infrastructure (qdev)
+# ****************************
+# Device infrastructure (qdev)
+# ****************************
 ##
 
 { 'include': 'qom.json' }
diff --git a/qapi/qom.json b/qapi/qom.json
index 3e8debf78c2..c8fe0258a5f 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -10,7 +10,9 @@
 { 'include': 'crypto.json' }
 
 ##
-# = QEMU Object Model (QOM)
+# ***********************
+# QEMU Object Model (QOM)
+# ***********************
 ##
 
 ##
diff --git a/qapi/replay.json b/qapi/replay.json
index 35e0c4a6926..e46c5c1d3f3 100644
--- a/qapi/replay.json
+++ b/qapi/replay.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Record/replay
+# *************
+# Record/replay
+# *************
 ##
 
 { 'include': 'common.json' }
diff --git a/qapi/rocker.json b/qapi/rocker.json
index 0c7ef1f77c8..e4949649526 100644
--- a/qapi/rocker.json
+++ b/qapi/rocker.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = Rocker switch device
+# ********************
+# Rocker switch device
+# ********************
 ##
 
 ##
diff --git a/qapi/run-state.json b/qapi/run-state.json
index fd09beb35cb..083a3c5eb30 100644
--- a/qapi/run-state.json
+++ b/qapi/run-state.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = VM run state
+# ************
+# VM run state
+# ************
 ##
 
 ##
diff --git a/qapi/sockets.json b/qapi/sockets.json
index f9f559dabae..b5f4a8fecda 100644
--- a/qapi/sockets.json
+++ b/qapi/sockets.json
@@ -2,7 +2,9 @@
 # vim: filetype=python
 
 ##
-# = Socket data types
+# *****************
+# Socket data types
+# *****************
 ##
 
 ##
diff --git a/qapi/stats.json b/qapi/stats.json
index 8902ef94e08..78b88881eab 100644
--- a/qapi/stats.json
+++ b/qapi/stats.json
@@ -9,7 +9,9 @@
 # SPDX-License-Identifier: GPL-2.0-or-later
 
 ##
-# = Statistics
+# **********
+# Statistics
+# **********
 ##
 
 ##
diff --git a/qapi/tpm.json b/qapi/tpm.json
index a16a72edb98..e66b107f1d0 100644
--- a/qapi/tpm.json
+++ b/qapi/tpm.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = TPM (trusted platform module) devices
+# *************************************
+# TPM (trusted platform module) devices
+# *************************************
 ##
 
 ##
diff --git a/qapi/trace.json b/qapi/trace.json
index eb5f63f5135..d08c9c6a889 100644
--- a/qapi/trace.json
+++ b/qapi/trace.json
@@ -7,7 +7,9 @@
 # See the COPYING file in the top-level directory.
 
 ##
-# = Tracing
+# *******
+# Tracing
+# *******
 ##
 
 ##
diff --git a/qapi/transaction.json b/qapi/transaction.json
index 9d9e7af26cb..927035f5a7e 100644
--- a/qapi/transaction.json
+++ b/qapi/transaction.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Transactions
+# ************
+# Transactions
+# ************
 ##
 
 { 'include': 'block-core.json' }
diff --git a/qapi/uefi.json b/qapi/uefi.json
index 6592183d6cf..a206c2e9539 100644
--- a/qapi/uefi.json
+++ b/qapi/uefi.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = UEFI Variable Store
+# *******************
+# UEFI Variable Store
+# *******************
 #
 # The QEMU efi variable store implementation (hw/uefi/) uses this to
 # store non-volatile variables in json format on disk.
diff --git a/qapi/ui.json b/qapi/ui.json
index 514fa159b10..f5e77ae19d7 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Remote desktop
+# **************
+# Remote desktop
+# **************
 ##
 
 { 'include': 'common.json' }
@@ -200,7 +202,8 @@
   'if': 'CONFIG_PIXMAN' }
 
 ##
-# == Spice
+# Spice
+# =====
 ##
 
 ##
@@ -461,7 +464,8 @@
   'if': 'CONFIG_SPICE' }
 
 ##
-# == VNC
+# VNC
+# ===
 ##
 
 ##
@@ -794,7 +798,9 @@
   'if': 'CONFIG_VNC' }
 
 ##
-# = Input
+# *****
+# Input
+# *****
 ##
 
 ##
diff --git a/qapi/vfio.json b/qapi/vfio.json
index b53b7caecd7..a1a9c5b673d 100644
--- a/qapi/vfio.json
+++ b/qapi/vfio.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = VFIO devices
+# ************
+# VFIO devices
+# ************
 ##
 
 ##
diff --git a/qapi/virtio.json b/qapi/virtio.json
index 73df718a261..3cac0774f7a 100644
--- a/qapi/virtio.json
+++ b/qapi/virtio.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Virtio devices
+# **************
+# Virtio devices
+# **************
 ##
 
 ##
diff --git a/qapi/yank.json b/qapi/yank.json
index 30f46c97c98..d13a8e91171 100644
--- a/qapi/yank.json
+++ b/qapi/yank.json
@@ -3,7 +3,9 @@
 #
 
 ##
-# = Yank feature
+# ************
+# Yank feature
+# ************
 ##
 
 ##
diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index 949d9e8bff7..aad7e249f86 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -597,22 +597,15 @@ def get_doc(self) -> 'QAPIDoc':
             # Free-form documentation
             doc = QAPIDoc(info)
             doc.ensure_untagged_section(self.info)
-            first = True
             while line is not None:
                 if match := self._match_at_name_colon(line):
                     raise QAPIParseError(
                         self,
                         "'@%s:' not allowed in free-form documentation"
                         % match.group(1))
-                if line.startswith('='):
-                    if not first:
-                        raise QAPIParseError(
-                            self,
-                            "'=' heading must come first in a comment block")
                 doc.append_line(line)
                 self.accept(False)
                 line = self.get_doc_line()
-                first = False
 
         self.accept()
         doc.end()
diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json
index 0427594c984..478e7a92b21 100644
--- a/storage-daemon/qapi/qapi-schema.json
+++ b/storage-daemon/qapi/qapi-schema.json
@@ -14,7 +14,9 @@
 # storage daemon.
 
 ##
-# = Introduction
+# ************
+# Introduction
+# ************
 #
 # This manual describes the commands and events supported by the QEMU
 # storage daemon QMP.
@@ -51,7 +53,9 @@
 { 'include': '../../qapi/job.json' }
 
 ##
-# = Block devices
+# *************
+# Block devices
+# *************
 ##
 { 'include': '../../qapi/block-core.json' }
 { 'include': '../../qapi/block-export.json' }
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 14b808f9090..fac13425b72 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -8,7 +8,9 @@
     'documentation-exceptions': [ 'Enum', 'Variant1', 'Alternate', 'cmd' ] } }
 
 ##
-# = Section
+# *******
+# Section
+# *******
 ##
 
 ##
@@ -16,7 +18,8 @@
 ##
 
 ##
-# == Subsection
+# Subsection
+# ==========
 #
 # *with emphasis*
 # @var {in braces}
@@ -144,7 +147,8 @@
   'if': { 'not': { 'any': [ 'IFONE', 'IFTWO' ] } } }
 
 ##
-# == Another subsection
+# Another subsection
+# ==================
 ##
 
 ##
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out
index dc8352eed4f..04a55072646 100644
--- a/tests/qapi-schema/doc-good.out
+++ b/tests/qapi-schema/doc-good.out
@@ -55,13 +55,16 @@ event EVT_BOXED Object
     feature feat3
 doc freeform
     body=
-= Section
+*******
+Section
+*******
 doc freeform
     body=
 Just text, no heading.
 doc freeform
     body=
-== Subsection
+Subsection
+==========
 
 *with emphasis*
 @var {in braces}
@@ -155,7 +158,8 @@ description starts on the same line
 a feature
 doc freeform
     body=
-== Another subsection
+Another subsection
+==================
 doc symbol=cmd
     body=
 
-- 
2.48.1
Re: [PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections
Posted by Markus Armbruster 5 months, 3 weeks ago
John Snow <jsnow@redhat.com> writes:

> This change removes special parsing for freeform sections and allows
> them to simply be unmodified rST syntax. The existing headings in the
> QAPI schema are adjusted to reflect the new paradigm.

"Allows them to" suggests the patch enables use of rST headings.  Is
this the case?  Or do they just work, and this patch just switches
schema code to use them, and drops now unnecessary generator code?
>
> Tests and documentation are updated to match.
>
> Signed-off-by: John Snow <jsnow@redhat.com>

[...]

> diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
> index 231cc0fecf7..dfdbeac5a5a 100644
> --- a/docs/devel/qapi-code-gen.rst
> +++ b/docs/devel/qapi-code-gen.rst
> @@ -876,25 +876,35 @@ structuring content.
   Documentation comments
   ----------------------

   A multi-line comment that starts and ends with a ``##`` line is a
   documentation comment.

   If the documentation comment starts like ::

       ##
       # @SYMBOL:

   it documents the definition of SYMBOL, else it's free-form
   documentation.

   See below for more on `Definition documentation`_.

   Free-form documentation may be used to provide additional text and
   structuring content.


>  Headings and subheadings
>  ~~~~~~~~~~~~~~~~~~~~~~~~
>  
> -A free-form documentation comment containing a line which starts with
> -some ``=`` symbols and then a space defines a section heading::
> +Free-form documentation does not start with ``@SYMBOL`` and can contain
> +arbitrary rST markup. Headings can be marked up using the standard rST
> +syntax::

Nothing stops you from using such markup in definition documentation.
It's probably a bad idea, though.

I think it's easiest not to talk about the two kinds of doc blocks here
at all.  Scratch the first sentence?

>  
>      ##
> -    # = This is a top level heading
> +    # *************************
> +    # This is a level 2 heading
> +    # *************************
>      #
>      # This is a free-form comment which will go under the
>      # top level heading.
>      ##
>  
>      ##
> -    # == This is a second level heading
> +    # This is a third level heading
> +    # ==============================
> +    #
> +    # Level 4
> +    # _______
> +    #
> +    # Level 5
> +    # ^^^^^^^
> +    #
> +    # Level 6
> +    # """""""
>      ##
>  
> -A heading line must be the first line of the documentation
> -comment block.
> -
> -Section headings must always be correctly nested, so you can only
> -define a third-level heading inside a second-level heading, and so on.
> +Level 1 headings are reserved for use by the generated documentation
> +page itself, leaving level 2 as the highest level that should be used.
>  
>  
>  Documentation markup

[...]
Re: [PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections
Posted by John Snow 5 months, 2 weeks ago
On Fri, Jun 27, 2025, 5:52 AM Markus Armbruster <armbru@redhat.com> wrote:

> John Snow <jsnow@redhat.com> writes:
>
> > This change removes special parsing for freeform sections and allows
> > them to simply be unmodified rST syntax. The existing headings in the
> > QAPI schema are adjusted to reflect the new paradigm.
>
> "Allows them to" suggests the patch enables use of rST headings.  Is
> this the case?  Or do they just work, and this patch just switches
> schema code to use them, and drops now unnecessary generator code?
>

Ehm... Kind of both? I guess I hadn't considered that rST headings might
already work without the switch. I guess they didn't because of the
headerless freeform section bug I fix in this series.

I guess you're technically right, I just never thought of it in that way.

I'll update the message so I don't confuse you in the future.

>
> > Tests and documentation are updated to match.
> >
> > Signed-off-by: John Snow <jsnow@redhat.com>
>
> [...]
>
> > diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
> > index 231cc0fecf7..dfdbeac5a5a 100644
> > --- a/docs/devel/qapi-code-gen.rst
> > +++ b/docs/devel/qapi-code-gen.rst
> > @@ -876,25 +876,35 @@ structuring content.
>    Documentation comments
>    ----------------------
>
>    A multi-line comment that starts and ends with a ``##`` line is a
>    documentation comment.
>
>    If the documentation comment starts like ::
>
>        ##
>        # @SYMBOL:
>
>    it documents the definition of SYMBOL, else it's free-form
>    documentation.
>
>    See below for more on `Definition documentation`_.
>
>    Free-form documentation may be used to provide additional text and
>    structuring content.
>
>
> >  Headings and subheadings
> >  ~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > -A free-form documentation comment containing a line which starts with
> > -some ``=`` symbols and then a space defines a section heading::
> > +Free-form documentation does not start with ``@SYMBOL`` and can contain
> > +arbitrary rST markup. Headings can be marked up using the standard rST
> > +syntax::
>
> Nothing stops you from using such markup in definition documentation.
> It's probably a bad idea, though.
>
> I think it's easiest not to talk about the two kinds of doc blocks here
> at all.  Scratch the first sentence?
>
> >
> >      ##
> > -    # = This is a top level heading
> > +    # *************************
> > +    # This is a level 2 heading
> > +    # *************************
> >      #
> >      # This is a free-form comment which will go under the
> >      # top level heading.
> >      ##
> >
> >      ##
> > -    # == This is a second level heading
> > +    # This is a third level heading
> > +    # ==============================
> > +    #
> > +    # Level 4
> > +    # _______
> > +    #
> > +    # Level 5
> > +    # ^^^^^^^
> > +    #
> > +    # Level 6
> > +    # """""""
> >      ##
> >
> > -A heading line must be the first line of the documentation
> > -comment block.
> > -
> > -Section headings must always be correctly nested, so you can only
> > -define a third-level heading inside a second-level heading, and so on.
> > +Level 1 headings are reserved for use by the generated documentation
> > +page itself, leaving level 2 as the highest level that should be used.
> >
> >
> >  Documentation markup
>
> [...]
>
>
Re: [PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections
Posted by Markus Armbruster 5 months, 2 weeks ago
John Snow <jsnow@redhat.com> writes:

> On Fri, Jun 27, 2025, 5:52 AM Markus Armbruster <armbru@redhat.com> wrote:
>
>> John Snow <jsnow@redhat.com> writes:
>>
>> > This change removes special parsing for freeform sections and allows
>> > them to simply be unmodified rST syntax. The existing headings in the
>> > QAPI schema are adjusted to reflect the new paradigm.
>>
>> "Allows them to" suggests the patch enables use of rST headings.  Is
>> this the case?  Or do they just work, and this patch just switches
>> schema code to use them, and drops now unnecessary generator code?
>>
>
> Ehm... Kind of both? I guess I hadn't considered that rST headings might
> already work without the switch. I guess they didn't because of the
> headerless freeform section bug I fix in this series.

To double-check master, I use

diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 14b808f909..201462688f 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -8,7 +8,9 @@
     'documentation-exceptions': [ 'Enum', 'Variant1', 'Alternate', 'cmd' ] } }
 
 ##
-# = Section
+# *******
+# Section
+# *******
 ##
 
 ##

Doesn't work with the legacy doc generator:

    Exception occurred:
      File "/work/armbru/qemu/docs/sphinx/qapidoc_legacy.py", line 360, in _start_new_heading
        raise QAPISemError(self._cur_doc.info,
        ...<2 lines>...
                           % (level, level - 1))
    qapi.error.QAPISemError: /work/armbru/qemu/docs/../tests/qapi-schema/doc-good.json:20: Level 2 subheading found outside a level 1 heading

So switch to the new one:

diff --git a/tests/qapi-schema/doc-good.rst b/tests/qapi-schema/doc-good.rst
index 1e4c23305a..9978ac2e9c 100644
--- a/tests/qapi-schema/doc-good.rst
+++ b/tests/qapi-schema/doc-good.rst
@@ -3,3 +3,4 @@
    a plain-text output file and compare it against a reference.
 
 .. qapi-doc:: tests/qapi-schema/doc-good.json
+   :transmogrify:

Chokes on "# Errors: some", you fix this (unrelated) bug in PATCH 1.  I
could apply that fix, but instead I simply work around the bug:

diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 14b808f909..201462688f 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -165,7 +167,8 @@
 #
 # Returns: @Object
 #
-# Errors: some
+# Errors:
+#     some
 #
 # TODO: frobnicate
 #

The rST heading seems to work fine then.

The next-level heading

diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json
index 14b808f9090..fac13425b72 100644
--- a/tests/qapi-schema/doc-good.json
+++ b/tests/qapi-schema/doc-good.json
@@ -16,7 +18,8 @@
 ##
 
 ##
-# == Subsection
+# Subsection
+# ==========
 #
 # *with emphasis*
 # @var {in braces}

doesn't:

    Extension error:
    /work/armbru/qemu/docs/../tests/qapi-schema/doc-good.json:22:1: '=' heading must come first in a comment block

Removed in this patch.

So you're right, "kind of both": they work just fine with the new doc
generator, except for the "====" rST headings, because the parser still
recognizes them as old-style schema doc headings.

> I guess you're technically right, I just never thought of it in that way.
>
> I'll update the message so I don't confuse you in the future.

Yes, please.

[...]
Re: [PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections
Posted by Markus Armbruster 5 months ago
Markus Armbruster <armbru@redhat.com> writes:

> John Snow <jsnow@redhat.com> writes:
>
>> On Fri, Jun 27, 2025, 5:52 AM Markus Armbruster <armbru@redhat.com> wrote:
>>
>>> John Snow <jsnow@redhat.com> writes:
>>>
>>> > This change removes special parsing for freeform sections and allows
>>> > them to simply be unmodified rST syntax. The existing headings in the
>>> > QAPI schema are adjusted to reflect the new paradigm.
>>>
>>> "Allows them to" suggests the patch enables use of rST headings.  Is
>>> this the case?  Or do they just work, and this patch just switches
>>> schema code to use them, and drops now unnecessary generator code?
>>>
>>
>> Ehm... Kind of both? I guess I hadn't considered that rST headings might
>> already work without the switch. I guess they didn't because of the
>> headerless freeform section bug I fix in this series.

[...]

> So you're right, "kind of both": they work just fine with the new doc
> generator, except for the "====" rST headings, because the parser still
> recognizes them as old-style schema doc headings.
>
>> I guess you're technically right, I just never thought of it in that way.
>>
>> I'll update the message so I don't confuse you in the future.
>
> Yes, please.
>
> [...]

I'm going with

    docs/sphinx: remove special parsing for freeform sections

    Remove the QAPI doc section heading syntax, use plain rST section
    headings instead.

    Tests and documentation are updated to match.

    Interestingly, Plain rST headings work fine before this patch, except
    for over- and underlining with '=', which the doc parser rejected as
    invalid QAPI doc section heading in free-form comments.
Re: [PATCH v3 4/5] docs/sphinx: remove special parsing for freeform sections
Posted by John Snow 5 months, 2 weeks ago
On Fri, Jun 27, 2025, 5:52 AM Markus Armbruster <armbru@redhat.com> wrote:

> John Snow <jsnow@redhat.com> writes:
>
> > This change removes special parsing for freeform sections and allows
> > them to simply be unmodified rST syntax. The existing headings in the
> > QAPI schema are adjusted to reflect the new paradigm.
>
> "Allows them to" suggests the patch enables use of rST headings.  Is
> this the case?  Or do they just work, and this patch just switches
> schema code to use them, and drops now unnecessary generator code?
> >
> > Tests and documentation are updated to match.
> >
> > Signed-off-by: John Snow <jsnow@redhat.com>
>
> [...]
>
> > diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst
> > index 231cc0fecf7..dfdbeac5a5a 100644
> > --- a/docs/devel/qapi-code-gen.rst
> > +++ b/docs/devel/qapi-code-gen.rst
> > @@ -876,25 +876,35 @@ structuring content.
>    Documentation comments
>    ----------------------
>
>    A multi-line comment that starts and ends with a ``##`` line is a
>    documentation comment.
>
>    If the documentation comment starts like ::
>
>        ##
>        # @SYMBOL:
>
>    it documents the definition of SYMBOL, else it's free-form
>    documentation.
>
>    See below for more on `Definition documentation`_.
>
>    Free-form documentation may be used to provide additional text and
>    structuring content.
>
>
> >  Headings and subheadings
> >  ~~~~~~~~~~~~~~~~~~~~~~~~
> >
> > -A free-form documentation comment containing a line which starts with
> > -some ``=`` symbols and then a space defines a section heading::
> > +Free-form documentation does not start with ``@SYMBOL`` and can contain
> > +arbitrary rST markup. Headings can be marked up using the standard rST
> > +syntax::
>
> Nothing stops you from using such markup in definition documentation.
> It's probably a bad idea, though.
>
> I think it's easiest not to talk about the two kinds of doc blocks here
> at all.  Scratch the first sentence?
>

Sure.


> >
> >      ##
> > -    # = This is a top level heading
> > +    # *************************
> > +    # This is a level 2 heading
> > +    # *************************
> >      #
> >      # This is a free-form comment which will go under the
> >      # top level heading.
> >      ##
> >
> >      ##
> > -    # == This is a second level heading
> > +    # This is a third level heading
> > +    # ==============================
> > +    #
> > +    # Level 4
> > +    # _______
> > +    #
> > +    # Level 5
> > +    # ^^^^^^^
> > +    #
> > +    # Level 6
> > +    # """""""
> >      ##
> >
> > -A heading line must be the first line of the documentation
> > -comment block.
> > -
> > -Section headings must always be correctly nested, so you can only
> > -define a third-level heading inside a second-level heading, and so on.
> > +Level 1 headings are reserved for use by the generated documentation
> > +page itself, leaving level 2 as the highest level that should be used.
> >
> >
> >  Documentation markup
>
> [...]
>
>