summaryrefslogtreecommitdiffstats
path: root/doc/extdev/markupapi.rst
blob: 7aa632446da956a377b87045f8660d1eaf12bf66 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
Docutils markup API
===================

This section describes the API for adding reStructuredText markup elements
(roles and directives).


Roles
-----

Roles follow the interface described below.
They have to be registered by an extension using
:meth:`.Sphinx.add_role` or :meth:`.Sphinx.add_role_to_domain`.


.. code-block:: python

   def role_function(
       role_name: str, raw_source: str, text: str,
       lineno: int, inliner: Inliner,
       options: dict = {}, content: list = [],
   ) -> tuple[list[Node], list[system_message]]:
       elements = []
       messages = []
       return elements, messages

The *options* and *content* parameters are only used for custom roles
created via the :dudir:`role` directive.
The return value is a tuple of two lists,
the first containing the text nodes and elements from the role,
and the second containing any system messages generated.
For more information, see the `custom role overview`_ from Docutils.

.. _custom role overview: https://docutils.sourceforge.io/docs/howto/rst-roles.html


Creating custom roles
^^^^^^^^^^^^^^^^^^^^^

Sphinx provides two base classes for creating custom roles,
:class:`~sphinx.util.docutils.SphinxRole` and :class:`~sphinx.util.docutils.ReferenceRole`.

These provide a class-based interface for creating roles,
where the main logic must be implemented in your ``run()`` method.
The classes provide a number of useful methods and attributes,
such as ``self.text``, ``self.config``, and ``self.env``.
The ``ReferenceRole`` class implements Sphinx's ``title <target>`` logic,
exposing ``self.target`` and ``self.title`` attributes.
This is useful for creating cross-reference roles.


Directives
----------

Directives are handled by classes derived from
``docutils.parsers.rst.Directive``.  They have to be registered by an extension
using :meth:`.Sphinx.add_directive` or :meth:`.Sphinx.add_directive_to_domain`.

.. module:: docutils.parsers.rst

.. class:: Directive

   The markup syntax of the new directive is determined by the follow five class
   attributes:

   .. autoattribute:: required_arguments
   .. autoattribute:: optional_arguments
   .. autoattribute:: final_argument_whitespace
   .. autoattribute:: option_spec

      Option validator functions take a single parameter, the option argument
      (or ``None`` if not given), and should validate it or convert it to the
      proper form.  They raise :exc:`ValueError` or :exc:`TypeError` to indicate
      failure.

      There are several predefined and possibly useful validators in the
      :mod:`docutils.parsers.rst.directives` module.

   .. autoattribute:: has_content

   New directives must implement the :meth:`run` method:

   .. method:: run()

      This method must process the directive arguments, options and content, and
      return a list of Docutils/Sphinx nodes that will be inserted into the
      document tree at the point where the directive was encountered.

   Instance attributes that are always set on the directive are:

   .. attribute:: name

      The directive name (useful when registering the same directive class under
      multiple names).

   .. attribute:: arguments

      The arguments given to the directive, as a list.

   .. attribute:: options

      The options given to the directive, as a dictionary mapping option names
      to validated/converted values.

   .. attribute:: content

      The directive content, if given, as a :class:`!ViewList`.

   .. attribute:: lineno

      The absolute line number on which the directive appeared.  This is not
      always a useful value; use :attr:`srcline` instead.

   .. attribute:: content_offset

      Internal offset of the directive content.  Used when calling
      ``nested_parse`` (see below).

   .. attribute:: block_text

      The string containing the entire directive.

   .. attribute:: state
                  state_machine

      The state and state machine which controls the parsing.  Used for
      ``nested_parse``.

.. seealso::

   `Creating directives`_ HOWTO of the Docutils documentation

   .. _Creating directives: https://docutils.sourceforge.io/docs/howto/rst-directives.html


.. _parsing-directive-content-as-rest:

Parsing directive content as reStructuredText
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Many directives will contain more markup that must be parsed.
To do this, use one of the following APIs from the :meth:`~Directive.run` method:

* :py:meth:`.SphinxDirective.parse_content_to_nodes()`
* :py:meth:`.SphinxDirective.parse_text_to_nodes()`

The first method parses all the directive's content as markup,
whilst the second only parses the given *text* string.
Both methods return the parsed Docutils nodes in a list.

The methods are used as follows:

.. code-block:: python

   def run(self) -> list[Node]:
       # either
       parsed = self.parse_content_to_nodes()
       # or
       parsed = self.parse_text_to_nodes('spam spam spam')
       return parsed

.. note::

   The above utility methods were added in Sphinx 7.4.
   Prior to Sphinx 7.4, the following methods should be used to parse content:

   * ``self.state.nested_parse``
   * :func:`sphinx.util.nodes.nested_parse_with_titles` -- this allows titles in
     the parsed content.

   .. code-block:: python

      def run(self) -> list[Node]:
          container = docutils.nodes.Element()
          # either
          nested_parse_with_titles(self.state, self.result, container)
          # or
          self.state.nested_parse(self.result, 0, container)
          parsed = container.children
          return parsed

To parse inline markup,
use :py:meth:`~sphinx.util.docutils.SphinxDirective.parse_inline()`.
This must only be used for text which is a single line or paragraph,
and does not contain any structural elements
(headings, transitions, directives, etc).

.. note::

   ``sphinx.util.docutils.switch_source_input()`` allows changing
   the source (input) file during parsing content in a directive.
   It is useful to parse mixed content, such as in ``sphinx.ext.autodoc``,
   where it is used to parse docstrings.

   .. code-block:: python

      from sphinx.util.docutils import switch_source_input
      from sphinx.util.parsing import nested_parse_to_nodes

      # Switch source_input between parsing content.
      # Inside this context, all parsing errors and warnings are reported as
      # happened in new source_input (in this case, ``self.result``).
      with switch_source_input(self.state, self.result):
          parsed = nested_parse_to_nodes(self.state, self.result)

   .. deprecated:: 1.7

      Until Sphinx 1.6, ``sphinx.ext.autodoc.AutodocReporter`` was used for this
      purpose.  It is replaced by ``switch_source_input()``.


.. _ViewLists:

ViewLists and StringLists
^^^^^^^^^^^^^^^^^^^^^^^^^

Docutils represents document source lines in a ``StringList`` class,
which inherits from ``ViewList``, both in the ``docutils.statemachine`` module.
This is a list with extended functionality,
including that slicing creates views of the original list and
that the list contains information about source line numbers.

The :attr:`Directive.content` attribute is a ``StringList``.
If you generate content to be parsed as reStructuredText,
you have to create a ``StringList`` for the Docutils APIs.
The utility functions provided by Sphinx handle this automatically.
Important for content generation are the following points:

* The ``ViewList`` constructor takes a list of strings (lines)
  and a source (document) name.
* The ``ViewList.append()`` method takes a line and a source name as well.