xref: /linux/Documentation/sphinx/cdomain.py (revision d56b699d76d1b352f7a3d3a0a3e91c79b8612d94)
1e8f5c617SMarkus Heiser# -*- coding: utf-8; mode: python -*-
256cd8692SMarkus Heiser# pylint: disable=W0141,C0113,C0103,C0325
3e8f5c617SMarkus Heiseru"""
4e8f5c617SMarkus Heiser    cdomain
5e8f5c617SMarkus Heiser    ~~~~~~~
6e8f5c617SMarkus Heiser
7e8f5c617SMarkus Heiser    Replacement for the sphinx c-domain.
8e8f5c617SMarkus Heiser
9e8f5c617SMarkus Heiser    :copyright:  Copyright (C) 2016  Markus Heiser
10e8f5c617SMarkus Heiser    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
112c645cd7SMarkus Heiser
122c645cd7SMarkus Heiser    List of customizations:
132c645cd7SMarkus Heiser
14556aa6d5SMarkus Heiser    * Moved the *duplicate C object description* warnings for function
15556aa6d5SMarkus Heiser      declarations in the nitpicky mode. See Sphinx documentation for
16556aa6d5SMarkus Heiser      the config values for ``nitpick`` and ``nitpick_ignore``.
17556aa6d5SMarkus Heiser
182c645cd7SMarkus Heiser    * Add option 'name' to the "c:function:" directive.  With option 'name' the
192c645cd7SMarkus Heiser      ref-name of a function can be modified. E.g.::
202c645cd7SMarkus Heiser
212c645cd7SMarkus Heiser          .. c:function:: int ioctl( int fd, int request )
222c645cd7SMarkus Heiser             :name: VIDIOC_LOG_STATUS
232c645cd7SMarkus Heiser
242c645cd7SMarkus Heiser      The func-name (e.g. ioctl) remains in the output but the ref-name changed
252c645cd7SMarkus Heiser      from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by::
262c645cd7SMarkus Heiser
272c645cd7SMarkus Heiser          * :c:func:`VIDIOC_LOG_STATUS` or
282c645cd7SMarkus Heiser          * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3)
2956cd8692SMarkus Heiser
3056cd8692SMarkus Heiser     * Handle signatures of function-like macros well. Don't try to deduce
3156cd8692SMarkus Heiser       arguments types of function-like macros.
3256cd8692SMarkus Heiser
33e8f5c617SMarkus Heiser"""
34e8f5c617SMarkus Heiser
3556cd8692SMarkus Heiserfrom docutils import nodes
362c645cd7SMarkus Heiserfrom docutils.parsers.rst import directives
372c645cd7SMarkus Heiser
38b495360eSMarkus Heiserimport sphinx
3956cd8692SMarkus Heiserfrom sphinx import addnodes
4056cd8692SMarkus Heiserfrom sphinx.domains.c import c_funcptr_sig_re, c_sig_re
41e8f5c617SMarkus Heiserfrom sphinx.domains.c import CObject as Base_CObject
42e8f5c617SMarkus Heiserfrom sphinx.domains.c import CDomain as Base_CDomain
4371e552aeSMauro Carvalho Chehabfrom itertools import chain
4471e552aeSMauro Carvalho Chehabimport re
45e8f5c617SMarkus Heiser
4671e552aeSMauro Carvalho Chehab__version__  = '1.1'
47e8f5c617SMarkus Heiser
48b495360eSMarkus Heiser# Get Sphinx version
49c46988aeSRémy Léonemajor, minor, patch = sphinx.version_info[:3]
50b495360eSMarkus Heiser
5171e552aeSMauro Carvalho Chehab# Namespace to be prepended to the full name
5271e552aeSMauro Carvalho Chehabnamespace = None
5371e552aeSMauro Carvalho Chehab
5471e552aeSMauro Carvalho Chehab#
5571e552aeSMauro Carvalho Chehab# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags
5671e552aeSMauro Carvalho Chehab# - Store the namespace if ".. c:namespace::" tag is found
5795f49490SMauro Carvalho Chehab#
5871e552aeSMauro Carvalho ChehabRE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
5971e552aeSMauro Carvalho Chehab
6071e552aeSMauro Carvalho Chehabdef markup_namespace(match):
6171e552aeSMauro Carvalho Chehab    global namespace
6271e552aeSMauro Carvalho Chehab
6371e552aeSMauro Carvalho Chehab    namespace = match.group(1)
6471e552aeSMauro Carvalho Chehab
6571e552aeSMauro Carvalho Chehab    return ""
6671e552aeSMauro Carvalho Chehab
6795f49490SMauro Carvalho Chehab#
6895f49490SMauro Carvalho Chehab# Handle c:macro for function-style declaration
6995f49490SMauro Carvalho Chehab#
7095f49490SMauro Carvalho ChehabRE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$')
7195f49490SMauro Carvalho Chehabdef markup_macro(match):
7295f49490SMauro Carvalho Chehab    return ".. c:function:: " + match.group(1) + ' ' + match.group(2)
7395f49490SMauro Carvalho Chehab
7495f49490SMauro Carvalho Chehab#
7595f49490SMauro Carvalho Chehab# Handle newer c domain tags that are evaluated as .. c:type: for
7695f49490SMauro Carvalho Chehab# backward-compatibility with Sphinx < 3.0
7795f49490SMauro Carvalho Chehab#
7895f49490SMauro Carvalho ChehabRE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$')
7995f49490SMauro Carvalho Chehab
8095f49490SMauro Carvalho Chehabdef markup_ctype(match):
8195f49490SMauro Carvalho Chehab    return ".. c:type:: " + match.group(2)
8295f49490SMauro Carvalho Chehab
8395f49490SMauro Carvalho Chehab#
8495f49490SMauro Carvalho Chehab# Handle newer c domain tags that are evaluated as :c:type: for
8595f49490SMauro Carvalho Chehab# backward-compatibility with Sphinx < 3.0
8695f49490SMauro Carvalho Chehab#
8795f49490SMauro Carvalho ChehabRE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`')
8895f49490SMauro Carvalho Chehabdef markup_ctype_refs(match):
8995f49490SMauro Carvalho Chehab    return ":c:type:`" + match.group(2) + '`'
9095f49490SMauro Carvalho Chehab
9195f49490SMauro Carvalho Chehab#
9295f49490SMauro Carvalho Chehab# Simply convert :c:expr: and :c:texpr: into a literal block.
9395f49490SMauro Carvalho Chehab#
9495f49490SMauro Carvalho ChehabRE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`')
9595f49490SMauro Carvalho Chehabdef markup_c_expr(match):
9695f49490SMauro Carvalho Chehab    return '\ ``' + match.group(2) + '``\ '
9795f49490SMauro Carvalho Chehab
9895f49490SMauro Carvalho Chehab#
9995f49490SMauro Carvalho Chehab# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones
10095f49490SMauro Carvalho Chehab#
10171e552aeSMauro Carvalho Chehabdef c_markups(app, docname, source):
10271e552aeSMauro Carvalho Chehab    result = ""
10371e552aeSMauro Carvalho Chehab    markup_func = {
10471e552aeSMauro Carvalho Chehab        RE_namespace: markup_namespace,
10595f49490SMauro Carvalho Chehab        RE_expr: markup_c_expr,
10695f49490SMauro Carvalho Chehab        RE_macro: markup_macro,
10795f49490SMauro Carvalho Chehab        RE_ctype: markup_ctype,
10895f49490SMauro Carvalho Chehab        RE_ctype_refs: markup_ctype_refs,
10971e552aeSMauro Carvalho Chehab    }
11071e552aeSMauro Carvalho Chehab
11171e552aeSMauro Carvalho Chehab    lines = iter(source[0].splitlines(True))
11271e552aeSMauro Carvalho Chehab    for n in lines:
11371e552aeSMauro Carvalho Chehab        match_iterators = [regex.finditer(n) for regex in markup_func]
11471e552aeSMauro Carvalho Chehab        matches = sorted(chain(*match_iterators), key=lambda m: m.start())
11571e552aeSMauro Carvalho Chehab        for m in matches:
11671e552aeSMauro Carvalho Chehab            n = n[:m.start()] + markup_func[m.re](m) + n[m.end():]
11771e552aeSMauro Carvalho Chehab
11871e552aeSMauro Carvalho Chehab        result = result + n
11971e552aeSMauro Carvalho Chehab
12071e552aeSMauro Carvalho Chehab    source[0] = result
12171e552aeSMauro Carvalho Chehab
12271e552aeSMauro Carvalho Chehab#
12371e552aeSMauro Carvalho Chehab# Now implements support for the cdomain namespacing logic
12471e552aeSMauro Carvalho Chehab#
12571e552aeSMauro Carvalho Chehab
126e8f5c617SMarkus Heiserdef setup(app):
127e8f5c617SMarkus Heiser
12871e552aeSMauro Carvalho Chehab    # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace::
12971e552aeSMauro Carvalho Chehab    app.connect('source-read', c_markups)
13071e552aeSMauro Carvalho Chehab
13142f6ebd8SMauro Carvalho Chehab    if (major == 1 and minor < 8):
132e8f5c617SMarkus Heiser        app.override_domain(CDomain)
13342f6ebd8SMauro Carvalho Chehab    else:
13442f6ebd8SMauro Carvalho Chehab        app.add_domain(CDomain, override=True)
135e8f5c617SMarkus Heiser
136e8f5c617SMarkus Heiser    return dict(
137e8f5c617SMarkus Heiser        version = __version__,
138e8f5c617SMarkus Heiser        parallel_read_safe = True,
139e8f5c617SMarkus Heiser        parallel_write_safe = True
140e8f5c617SMarkus Heiser    )
141e8f5c617SMarkus Heiser
142e8f5c617SMarkus Heiserclass CObject(Base_CObject):
143e8f5c617SMarkus Heiser
144e8f5c617SMarkus Heiser    """
145e8f5c617SMarkus Heiser    Description of a C language object.
146e8f5c617SMarkus Heiser    """
1472c645cd7SMarkus Heiser    option_spec = {
1482c645cd7SMarkus Heiser        "name" : directives.unchanged
1492c645cd7SMarkus Heiser    }
1502c645cd7SMarkus Heiser
15156cd8692SMarkus Heiser    def handle_func_like_macro(self, sig, signode):
15256cd8692SMarkus Heiser        u"""Handles signatures of function-like macros.
15356cd8692SMarkus Heiser
15456cd8692SMarkus Heiser        If the objtype is 'function' and the the signature ``sig`` is a
15556cd8692SMarkus Heiser        function-like macro, the name of the macro is returned. Otherwise
15656cd8692SMarkus Heiser        ``False`` is returned.  """
15756cd8692SMarkus Heiser
15871e552aeSMauro Carvalho Chehab        global namespace
15971e552aeSMauro Carvalho Chehab
16056cd8692SMarkus Heiser        if not self.objtype == 'function':
16156cd8692SMarkus Heiser            return False
16256cd8692SMarkus Heiser
16356cd8692SMarkus Heiser        m = c_funcptr_sig_re.match(sig)
16456cd8692SMarkus Heiser        if m is None:
16556cd8692SMarkus Heiser            m = c_sig_re.match(sig)
16656cd8692SMarkus Heiser            if m is None:
16756cd8692SMarkus Heiser                raise ValueError('no match')
16856cd8692SMarkus Heiser
16956cd8692SMarkus Heiser        rettype, fullname, arglist, _const = m.groups()
17056cd8692SMarkus Heiser        arglist = arglist.strip()
17156cd8692SMarkus Heiser        if rettype or not arglist:
17256cd8692SMarkus Heiser            return False
17356cd8692SMarkus Heiser
17456cd8692SMarkus Heiser        arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
17556cd8692SMarkus Heiser        arglist = [a.strip() for a in arglist.split(",")]
17656cd8692SMarkus Heiser
17756cd8692SMarkus Heiser        # has the first argument a type?
17856cd8692SMarkus Heiser        if len(arglist[0].split(" ")) > 1:
17956cd8692SMarkus Heiser            return False
18056cd8692SMarkus Heiser
181*d56b699dSBjorn Helgaas        # This is a function-like macro, its arguments are typeless!
18256cd8692SMarkus Heiser        signode  += addnodes.desc_name(fullname, fullname)
18356cd8692SMarkus Heiser        paramlist = addnodes.desc_parameterlist()
18456cd8692SMarkus Heiser        signode  += paramlist
18556cd8692SMarkus Heiser
18656cd8692SMarkus Heiser        for argname in arglist:
18756cd8692SMarkus Heiser            param = addnodes.desc_parameter('', '', noemph=True)
18856cd8692SMarkus Heiser            # separate by non-breaking space in the output
18956cd8692SMarkus Heiser            param += nodes.emphasis(argname, argname)
19056cd8692SMarkus Heiser            paramlist += param
19156cd8692SMarkus Heiser
19271e552aeSMauro Carvalho Chehab        if namespace:
19371e552aeSMauro Carvalho Chehab            fullname = namespace + "." + fullname
19471e552aeSMauro Carvalho Chehab
19556cd8692SMarkus Heiser        return fullname
19656cd8692SMarkus Heiser
1972c645cd7SMarkus Heiser    def handle_signature(self, sig, signode):
1982c645cd7SMarkus Heiser        """Transform a C signature into RST nodes."""
19956cd8692SMarkus Heiser
20071e552aeSMauro Carvalho Chehab        global namespace
20171e552aeSMauro Carvalho Chehab
20256cd8692SMarkus Heiser        fullname = self.handle_func_like_macro(sig, signode)
20356cd8692SMarkus Heiser        if not fullname:
2042c645cd7SMarkus Heiser            fullname = super(CObject, self).handle_signature(sig, signode)
20556cd8692SMarkus Heiser
2062c645cd7SMarkus Heiser        if "name" in self.options:
2072c645cd7SMarkus Heiser            if self.objtype == 'function':
2082c645cd7SMarkus Heiser                fullname = self.options["name"]
2092c645cd7SMarkus Heiser            else:
2102c645cd7SMarkus Heiser                # FIXME: handle :name: value of other declaration types?
2112c645cd7SMarkus Heiser                pass
21271e552aeSMauro Carvalho Chehab        else:
21371e552aeSMauro Carvalho Chehab            if namespace:
21471e552aeSMauro Carvalho Chehab                fullname = namespace + "." + fullname
21571e552aeSMauro Carvalho Chehab
2162c645cd7SMarkus Heiser        return fullname
2172c645cd7SMarkus Heiser
218556aa6d5SMarkus Heiser    def add_target_and_index(self, name, sig, signode):
219556aa6d5SMarkus Heiser        # for C API items we add a prefix since names are usually not qualified
220556aa6d5SMarkus Heiser        # by a module name and so easily clash with e.g. section titles
221556aa6d5SMarkus Heiser        targetname = 'c.' + name
222556aa6d5SMarkus Heiser        if targetname not in self.state.document.ids:
223556aa6d5SMarkus Heiser            signode['names'].append(targetname)
224556aa6d5SMarkus Heiser            signode['ids'].append(targetname)
225556aa6d5SMarkus Heiser            signode['first'] = (not self.names)
226556aa6d5SMarkus Heiser            self.state.document.note_explicit_target(signode)
227556aa6d5SMarkus Heiser            inv = self.env.domaindata['c']['objects']
228556aa6d5SMarkus Heiser            if (name in inv and self.env.config.nitpicky):
229556aa6d5SMarkus Heiser                if self.objtype == 'function':
230556aa6d5SMarkus Heiser                    if ('c:func', name) not in self.env.config.nitpick_ignore:
231556aa6d5SMarkus Heiser                        self.state_machine.reporter.warning(
232556aa6d5SMarkus Heiser                            'duplicate C object description of %s, ' % name +
233556aa6d5SMarkus Heiser                            'other instance in ' + self.env.doc2path(inv[name][0]),
234556aa6d5SMarkus Heiser                            line=self.lineno)
235556aa6d5SMarkus Heiser            inv[name] = (self.env.docname, self.objtype)
236556aa6d5SMarkus Heiser
237556aa6d5SMarkus Heiser        indextext = self.get_index_text(name)
238556aa6d5SMarkus Heiser        if indextext:
239b495360eSMarkus Heiser            self.indexnode['entries'].append(
240b495360eSMarkus Heiser                    ('single', indextext, targetname, '', None))
241e8f5c617SMarkus Heiser
242e8f5c617SMarkus Heiserclass CDomain(Base_CDomain):
243e8f5c617SMarkus Heiser
244e8f5c617SMarkus Heiser    """C language domain."""
245e8f5c617SMarkus Heiser    name = 'c'
246e8f5c617SMarkus Heiser    label = 'C'
247e8f5c617SMarkus Heiser    directives = {
248e8f5c617SMarkus Heiser        'function': CObject,
249e8f5c617SMarkus Heiser        'member':   CObject,
250e8f5c617SMarkus Heiser        'macro':    CObject,
251e8f5c617SMarkus Heiser        'type':     CObject,
252e8f5c617SMarkus Heiser        'var':      CObject,
253e8f5c617SMarkus Heiser    }
254