Merge tag 'drm-next-2023-09-08' of git://anongit.freedesktop.org/drm/drm
[linux-2.6-block.git] / Documentation / sphinx / cdomain.py
CommitLineData
e8f5c617 1# -*- coding: utf-8; mode: python -*-
56cd8692 2# pylint: disable=W0141,C0113,C0103,C0325
e8f5c617
MH
3u"""
4 cdomain
5 ~~~~~~~
6
7 Replacement for the sphinx c-domain.
8
9 :copyright: Copyright (C) 2016 Markus Heiser
10 :license: GPL Version 2, June 1991 see Linux/COPYING for details.
2c645cd7
MH
11
12 List of customizations:
13
556aa6d5
MH
14 * Moved the *duplicate C object description* warnings for function
15 declarations in the nitpicky mode. See Sphinx documentation for
16 the config values for ``nitpick`` and ``nitpick_ignore``.
17
2c645cd7
MH
18 * Add option 'name' to the "c:function:" directive. With option 'name' the
19 ref-name of a function can be modified. E.g.::
20
21 .. c:function:: int ioctl( int fd, int request )
22 :name: VIDIOC_LOG_STATUS
23
24 The func-name (e.g. ioctl) remains in the output but the ref-name changed
25 from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by::
26
27 * :c:func:`VIDIOC_LOG_STATUS` or
28 * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3)
56cd8692
MH
29
30 * Handle signatures of function-like macros well. Don't try to deduce
31 arguments types of function-like macros.
32
e8f5c617
MH
33"""
34
56cd8692 35from docutils import nodes
2c645cd7
MH
36from docutils.parsers.rst import directives
37
b495360e 38import sphinx
56cd8692
MH
39from sphinx import addnodes
40from sphinx.domains.c import c_funcptr_sig_re, c_sig_re
e8f5c617
MH
41from sphinx.domains.c import CObject as Base_CObject
42from sphinx.domains.c import CDomain as Base_CDomain
71e552ae
MCC
43from itertools import chain
44import re
e8f5c617 45
71e552ae 46__version__ = '1.1'
e8f5c617 47
b495360e 48# Get Sphinx version
c46988ae 49major, minor, patch = sphinx.version_info[:3]
b495360e 50
71e552ae
MCC
51# Namespace to be prepended to the full name
52namespace = None
53
54#
55# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags
56# - Store the namespace if ".. c:namespace::" tag is found
95f49490 57#
71e552ae
MCC
58RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
59
60def markup_namespace(match):
61 global namespace
62
63 namespace = match.group(1)
64
65 return ""
66
95f49490
MCC
67#
68# Handle c:macro for function-style declaration
69#
70RE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$')
71def markup_macro(match):
72 return ".. c:function:: " + match.group(1) + ' ' + match.group(2)
73
74#
75# Handle newer c domain tags that are evaluated as .. c:type: for
76# backward-compatibility with Sphinx < 3.0
77#
78RE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$')
79
80def markup_ctype(match):
81 return ".. c:type:: " + match.group(2)
82
83#
84# Handle newer c domain tags that are evaluated as :c:type: for
85# backward-compatibility with Sphinx < 3.0
86#
87RE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`')
88def markup_ctype_refs(match):
89 return ":c:type:`" + match.group(2) + '`'
90
91#
92# Simply convert :c:expr: and :c:texpr: into a literal block.
93#
94RE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`')
95def markup_c_expr(match):
96 return '\ ``' + match.group(2) + '``\ '
97
98#
99# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones
100#
71e552ae
MCC
101def c_markups(app, docname, source):
102 result = ""
103 markup_func = {
104 RE_namespace: markup_namespace,
95f49490
MCC
105 RE_expr: markup_c_expr,
106 RE_macro: markup_macro,
107 RE_ctype: markup_ctype,
108 RE_ctype_refs: markup_ctype_refs,
71e552ae
MCC
109 }
110
111 lines = iter(source[0].splitlines(True))
112 for n in lines:
113 match_iterators = [regex.finditer(n) for regex in markup_func]
114 matches = sorted(chain(*match_iterators), key=lambda m: m.start())
115 for m in matches:
116 n = n[:m.start()] + markup_func[m.re](m) + n[m.end():]
117
118 result = result + n
119
120 source[0] = result
121
122#
123# Now implements support for the cdomain namespacing logic
124#
125
e8f5c617
MH
126def setup(app):
127
71e552ae
MCC
128 # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace::
129 app.connect('source-read', c_markups)
130
42f6ebd8
MCC
131 if (major == 1 and minor < 8):
132 app.override_domain(CDomain)
133 else:
134 app.add_domain(CDomain, override=True)
e8f5c617
MH
135
136 return dict(
137 version = __version__,
138 parallel_read_safe = True,
139 parallel_write_safe = True
140 )
141
142class CObject(Base_CObject):
143
144 """
145 Description of a C language object.
146 """
2c645cd7
MH
147 option_spec = {
148 "name" : directives.unchanged
149 }
150
56cd8692
MH
151 def handle_func_like_macro(self, sig, signode):
152 u"""Handles signatures of function-like macros.
153
154 If the objtype is 'function' and the the signature ``sig`` is a
155 function-like macro, the name of the macro is returned. Otherwise
156 ``False`` is returned. """
157
71e552ae
MCC
158 global namespace
159
56cd8692
MH
160 if not self.objtype == 'function':
161 return False
162
163 m = c_funcptr_sig_re.match(sig)
164 if m is None:
165 m = c_sig_re.match(sig)
166 if m is None:
167 raise ValueError('no match')
168
169 rettype, fullname, arglist, _const = m.groups()
170 arglist = arglist.strip()
171 if rettype or not arglist:
172 return False
173
174 arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
175 arglist = [a.strip() for a in arglist.split(",")]
176
177 # has the first argument a type?
178 if len(arglist[0].split(" ")) > 1:
179 return False
180
d56b699d 181 # This is a function-like macro, its arguments are typeless!
56cd8692
MH
182 signode += addnodes.desc_name(fullname, fullname)
183 paramlist = addnodes.desc_parameterlist()
184 signode += paramlist
185
186 for argname in arglist:
187 param = addnodes.desc_parameter('', '', noemph=True)
188 # separate by non-breaking space in the output
189 param += nodes.emphasis(argname, argname)
190 paramlist += param
191
71e552ae
MCC
192 if namespace:
193 fullname = namespace + "." + fullname
194
56cd8692
MH
195 return fullname
196
2c645cd7
MH
197 def handle_signature(self, sig, signode):
198 """Transform a C signature into RST nodes."""
56cd8692 199
71e552ae
MCC
200 global namespace
201
56cd8692
MH
202 fullname = self.handle_func_like_macro(sig, signode)
203 if not fullname:
204 fullname = super(CObject, self).handle_signature(sig, signode)
205
2c645cd7
MH
206 if "name" in self.options:
207 if self.objtype == 'function':
208 fullname = self.options["name"]
209 else:
210 # FIXME: handle :name: value of other declaration types?
211 pass
71e552ae
MCC
212 else:
213 if namespace:
214 fullname = namespace + "." + fullname
215
2c645cd7
MH
216 return fullname
217
556aa6d5
MH
218 def add_target_and_index(self, name, sig, signode):
219 # for C API items we add a prefix since names are usually not qualified
220 # by a module name and so easily clash with e.g. section titles
221 targetname = 'c.' + name
222 if targetname not in self.state.document.ids:
223 signode['names'].append(targetname)
224 signode['ids'].append(targetname)
225 signode['first'] = (not self.names)
226 self.state.document.note_explicit_target(signode)
227 inv = self.env.domaindata['c']['objects']
228 if (name in inv and self.env.config.nitpicky):
229 if self.objtype == 'function':
230 if ('c:func', name) not in self.env.config.nitpick_ignore:
231 self.state_machine.reporter.warning(
232 'duplicate C object description of %s, ' % name +
233 'other instance in ' + self.env.doc2path(inv[name][0]),
234 line=self.lineno)
235 inv[name] = (self.env.docname, self.objtype)
236
237 indextext = self.get_index_text(name)
238 if indextext:
f546ff0c 239 self.indexnode['entries'].append(
b495360e 240 ('single', indextext, targetname, '', None))
e8f5c617
MH
241
242class CDomain(Base_CDomain):
243
244 """C language domain."""
245 name = 'c'
246 label = 'C'
247 directives = {
248 'function': CObject,
249 'member': CObject,
250 'macro': CObject,
251 'type': CObject,
252 'var': CObject,
253 }