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.

Signed-off-by: John Snow <jsnow@redhat.com>
Message-ID: <20250618165353.1980365-5-jsnow@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
[Add more detail to commit message]
Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
John Snow 2025-06-18 12:53:52 -04:00 committed by Markus Armbruster
parent 8d789c8cdb
commit 6c10778826
48 changed files with 173 additions and 106 deletions

View File

@ -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

View File

@ -11,7 +11,9 @@
# later. See the COPYING file in the top-level directory.
##
# = Firmware
# ********
# Firmware
# ********
##
{ 'pragma': {

View File

@ -10,7 +10,9 @@
# later. See the COPYING file in the top-level directory.
##
# = vhost user backend discovery & capabilities
# *******************************************
# vhost user backend discovery & capabilities
# *******************************************
##
##

View File

@ -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:

View File

@ -6,7 +6,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
##
# = ACPI
# ****
# ACPI
# ****
##
##

View File

@ -7,7 +7,9 @@
# See the COPYING file in the top-level directory.
##
# = Audio
# *****
# Audio
# *****
##
##

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = User authorization
# ******************
# User authorization
# ******************
##
##

View File

@ -2,7 +2,8 @@
# vim: filetype=python
##
# == Block core (VM unrelated)
# Block core (VM unrelated)
# =========================
##
{ 'include': 'common.json' }

View File

@ -2,7 +2,8 @@
# vim: filetype=python
##
# == Block device exports
# Block device exports
# ====================
##
{ 'include': 'sockets.json' }

View File

@ -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)
# ===================================
##
##

View File

@ -3,7 +3,9 @@
#
##
# = Character devices
# *****************
# Character devices
# *****************
##
{ 'include': 'sockets.json' }

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = Common data types
# *****************
# Common data types
# *****************
##
##

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = Compatibility policy
# ********************
# Compatibility policy
# ********************
##
##

View File

@ -3,7 +3,9 @@
#
##
# = QMP monitor control
# *******************
# QMP monitor control
# *******************
##
##

View File

@ -3,7 +3,9 @@
#
##
# = Cryptography
# ************
# Cryptography
# ************
##
##

View File

@ -5,7 +5,9 @@
# See the COPYING file in the top-level directory.
##
# = Cryptography devices
# ********************
# Cryptography devices
# ********************
##
##

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = CXL devices
# ***********
# CXL devices
# ***********
##
##

View File

@ -5,7 +5,9 @@
# See the COPYING file in the top-level directory.
##
# = Dump guest memory
# *****************
# Dump guest memory
# *****************
##
##

View File

@ -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

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = QMP errors
# **********
# QMP errors
# **********
##
##

View File

@ -10,7 +10,9 @@
# See the COPYING file in the top-level directory.
##
# = QMP introspection
# *****************
# QMP introspection
# *****************
##
##

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = Background jobs
# ***************
# Background jobs
# ***************
##
##

View File

@ -5,7 +5,9 @@
# See the COPYING file in the top-level directory.
##
# = Common machine types
# ********************
# Common machine types
# ********************
##
##

View File

@ -5,7 +5,9 @@
# See the COPYING file in the top-level directory.
##
# = Machines
# ********
# Machines
# ********
##
{ 'include': 'common.json' }

View File

@ -3,7 +3,9 @@
#
##
# = Migration
# *********
# Migration
# *********
##
{ 'include': 'common.json' }

View File

@ -3,7 +3,9 @@
#
##
# = Miscellanea
# ***********
# Miscellanea
# ***********
##
{ 'include': 'common.json' }

View File

@ -3,7 +3,9 @@
#
##
# = Net devices
# ***********
# Net devices
# ***********
##
{ 'include': 'sockets.json' }

View File

@ -6,7 +6,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
##
# = PCI
# ***
# PCI
# ***
##
##

View File

@ -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).

View File

@ -5,7 +5,9 @@
# See the COPYING file in the top-level directory.
##
# = Device infrastructure (qdev)
# ****************************
# Device infrastructure (qdev)
# ****************************
##
{ 'include': 'qom.json' }

View File

@ -10,7 +10,9 @@
{ 'include': 'crypto.json' }
##
# = QEMU Object Model (QOM)
# ***********************
# QEMU Object Model (QOM)
# ***********************
##
##

View File

@ -3,7 +3,9 @@
#
##
# = Record/replay
# *************
# Record/replay
# *************
##
{ 'include': 'common.json' }

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = Rocker switch device
# ********************
# Rocker switch device
# ********************
##
##

View File

@ -3,7 +3,9 @@
#
##
# = VM run state
# ************
# VM run state
# ************
##
##

View File

@ -2,7 +2,9 @@
# vim: filetype=python
##
# = Socket data types
# *****************
# Socket data types
# *****************
##
##

View File

@ -9,7 +9,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
##
# = Statistics
# **********
# Statistics
# **********
##
##

View File

@ -3,7 +3,9 @@
#
##
# = TPM (trusted platform module) devices
# *************************************
# TPM (trusted platform module) devices
# *************************************
##
##

View File

@ -7,7 +7,9 @@
# See the COPYING file in the top-level directory.
##
# = Tracing
# *******
# Tracing
# *******
##
##

View File

@ -3,7 +3,9 @@
#
##
# = Transactions
# ************
# Transactions
# ************
##
{ 'include': 'block-core.json' }

View File

@ -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.

View File

@ -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
# *****
##
##

View File

@ -3,7 +3,9 @@
#
##
# = VFIO devices
# ************
# VFIO devices
# ************
##
##

View File

@ -3,7 +3,9 @@
#
##
# = Virtio devices
# **************
# Virtio devices
# **************
##
##

View File

@ -3,7 +3,9 @@
#
##
# = Yank feature
# ************
# Yank feature
# ************
##
##

View File

@ -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()

View File

@ -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' }

View File

@ -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
# ==================
##
##

View File

@ -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=