xref: /linux/Documentation/sphinx/cdomain.py (revision 56cd869288f06cb59b81d3f75568e0d7dc89091f)
1e8f5c617SMarkus Heiser# -*- coding: utf-8; mode: python -*-
2*56cd8692SMarkus 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)
29*56cd8692SMarkus Heiser
30*56cd8692SMarkus Heiser     * Handle signatures of function-like macros well. Don't try to deduce
31*56cd8692SMarkus Heiser       arguments types of function-like macros.
32*56cd8692SMarkus Heiser
33e8f5c617SMarkus Heiser"""
34e8f5c617SMarkus Heiser
35*56cd8692SMarkus Heiserfrom docutils import nodes
362c645cd7SMarkus Heiserfrom docutils.parsers.rst import directives
372c645cd7SMarkus Heiser
38b495360eSMarkus Heiserimport sphinx
39*56cd8692SMarkus Heiserfrom sphinx import addnodes
40*56cd8692SMarkus 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
43e8f5c617SMarkus Heiser
44e8f5c617SMarkus Heiser__version__  = '1.0'
45e8f5c617SMarkus Heiser
46b495360eSMarkus Heiser# Get Sphinx version
47b495360eSMarkus Heisermajor, minor, patch = map(int, sphinx.__version__.split("."))
48b495360eSMarkus Heiser
49e8f5c617SMarkus Heiserdef setup(app):
50e8f5c617SMarkus Heiser
51e8f5c617SMarkus Heiser    app.override_domain(CDomain)
52e8f5c617SMarkus Heiser
53e8f5c617SMarkus Heiser    return dict(
54e8f5c617SMarkus Heiser        version = __version__,
55e8f5c617SMarkus Heiser        parallel_read_safe = True,
56e8f5c617SMarkus Heiser        parallel_write_safe = True
57e8f5c617SMarkus Heiser    )
58e8f5c617SMarkus Heiser
59e8f5c617SMarkus Heiserclass CObject(Base_CObject):
60e8f5c617SMarkus Heiser
61e8f5c617SMarkus Heiser    """
62e8f5c617SMarkus Heiser    Description of a C language object.
63e8f5c617SMarkus Heiser    """
642c645cd7SMarkus Heiser    option_spec = {
652c645cd7SMarkus Heiser        "name" : directives.unchanged
662c645cd7SMarkus Heiser    }
672c645cd7SMarkus Heiser
68*56cd8692SMarkus Heiser    def handle_func_like_macro(self, sig, signode):
69*56cd8692SMarkus Heiser        u"""Handles signatures of function-like macros.
70*56cd8692SMarkus Heiser
71*56cd8692SMarkus Heiser        If the objtype is 'function' and the the signature ``sig`` is a
72*56cd8692SMarkus Heiser        function-like macro, the name of the macro is returned. Otherwise
73*56cd8692SMarkus Heiser        ``False`` is returned.  """
74*56cd8692SMarkus Heiser
75*56cd8692SMarkus Heiser        if not self.objtype == 'function':
76*56cd8692SMarkus Heiser            return False
77*56cd8692SMarkus Heiser
78*56cd8692SMarkus Heiser        m = c_funcptr_sig_re.match(sig)
79*56cd8692SMarkus Heiser        if m is None:
80*56cd8692SMarkus Heiser            m = c_sig_re.match(sig)
81*56cd8692SMarkus Heiser            if m is None:
82*56cd8692SMarkus Heiser                raise ValueError('no match')
83*56cd8692SMarkus Heiser
84*56cd8692SMarkus Heiser        rettype, fullname, arglist, _const = m.groups()
85*56cd8692SMarkus Heiser        arglist = arglist.strip()
86*56cd8692SMarkus Heiser        if rettype or not arglist:
87*56cd8692SMarkus Heiser            return False
88*56cd8692SMarkus Heiser
89*56cd8692SMarkus Heiser        arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup
90*56cd8692SMarkus Heiser        arglist = [a.strip() for a in arglist.split(",")]
91*56cd8692SMarkus Heiser
92*56cd8692SMarkus Heiser        # has the first argument a type?
93*56cd8692SMarkus Heiser        if len(arglist[0].split(" ")) > 1:
94*56cd8692SMarkus Heiser            return False
95*56cd8692SMarkus Heiser
96*56cd8692SMarkus Heiser        # This is a function-like macro, it's arguments are typeless!
97*56cd8692SMarkus Heiser        signode  += addnodes.desc_name(fullname, fullname)
98*56cd8692SMarkus Heiser        paramlist = addnodes.desc_parameterlist()
99*56cd8692SMarkus Heiser        signode  += paramlist
100*56cd8692SMarkus Heiser
101*56cd8692SMarkus Heiser        for argname in arglist:
102*56cd8692SMarkus Heiser            param = addnodes.desc_parameter('', '', noemph=True)
103*56cd8692SMarkus Heiser            # separate by non-breaking space in the output
104*56cd8692SMarkus Heiser            param += nodes.emphasis(argname, argname)
105*56cd8692SMarkus Heiser            paramlist += param
106*56cd8692SMarkus Heiser
107*56cd8692SMarkus Heiser        return fullname
108*56cd8692SMarkus Heiser
1092c645cd7SMarkus Heiser    def handle_signature(self, sig, signode):
1102c645cd7SMarkus Heiser        """Transform a C signature into RST nodes."""
111*56cd8692SMarkus Heiser
112*56cd8692SMarkus Heiser        fullname = self.handle_func_like_macro(sig, signode)
113*56cd8692SMarkus Heiser        if not fullname:
1142c645cd7SMarkus Heiser            fullname = super(CObject, self).handle_signature(sig, signode)
115*56cd8692SMarkus Heiser
1162c645cd7SMarkus Heiser        if "name" in self.options:
1172c645cd7SMarkus Heiser            if self.objtype == 'function':
1182c645cd7SMarkus Heiser                fullname = self.options["name"]
1192c645cd7SMarkus Heiser            else:
1202c645cd7SMarkus Heiser                # FIXME: handle :name: value of other declaration types?
1212c645cd7SMarkus Heiser                pass
1222c645cd7SMarkus Heiser        return fullname
1232c645cd7SMarkus Heiser
124556aa6d5SMarkus Heiser    def add_target_and_index(self, name, sig, signode):
125556aa6d5SMarkus Heiser        # for C API items we add a prefix since names are usually not qualified
126556aa6d5SMarkus Heiser        # by a module name and so easily clash with e.g. section titles
127556aa6d5SMarkus Heiser        targetname = 'c.' + name
128556aa6d5SMarkus Heiser        if targetname not in self.state.document.ids:
129556aa6d5SMarkus Heiser            signode['names'].append(targetname)
130556aa6d5SMarkus Heiser            signode['ids'].append(targetname)
131556aa6d5SMarkus Heiser            signode['first'] = (not self.names)
132556aa6d5SMarkus Heiser            self.state.document.note_explicit_target(signode)
133556aa6d5SMarkus Heiser            inv = self.env.domaindata['c']['objects']
134556aa6d5SMarkus Heiser            if (name in inv and self.env.config.nitpicky):
135556aa6d5SMarkus Heiser                if self.objtype == 'function':
136556aa6d5SMarkus Heiser                    if ('c:func', name) not in self.env.config.nitpick_ignore:
137556aa6d5SMarkus Heiser                        self.state_machine.reporter.warning(
138556aa6d5SMarkus Heiser                            'duplicate C object description of %s, ' % name +
139556aa6d5SMarkus Heiser                            'other instance in ' + self.env.doc2path(inv[name][0]),
140556aa6d5SMarkus Heiser                            line=self.lineno)
141556aa6d5SMarkus Heiser            inv[name] = (self.env.docname, self.objtype)
142556aa6d5SMarkus Heiser
143556aa6d5SMarkus Heiser        indextext = self.get_index_text(name)
144556aa6d5SMarkus Heiser        if indextext:
145b495360eSMarkus Heiser            if major == 1 and minor < 4:
146b495360eSMarkus Heiser                # indexnode's tuple changed in 1.4
147b495360eSMarkus Heiser                # https://github.com/sphinx-doc/sphinx/commit/e6a5a3a92e938fcd75866b4227db9e0524d58f7c
148b495360eSMarkus Heiser                self.indexnode['entries'].append(
149b495360eSMarkus Heiser                    ('single', indextext, targetname, ''))
150b495360eSMarkus Heiser            else:
151b495360eSMarkus Heiser                self.indexnode['entries'].append(
152b495360eSMarkus Heiser                    ('single', indextext, targetname, '', None))
153e8f5c617SMarkus Heiser
154e8f5c617SMarkus Heiserclass CDomain(Base_CDomain):
155e8f5c617SMarkus Heiser
156e8f5c617SMarkus Heiser    """C language domain."""
157e8f5c617SMarkus Heiser    name = 'c'
158e8f5c617SMarkus Heiser    label = 'C'
159e8f5c617SMarkus Heiser    directives = {
160e8f5c617SMarkus Heiser        'function': CObject,
161e8f5c617SMarkus Heiser        'member':   CObject,
162e8f5c617SMarkus Heiser        'macro':    CObject,
163e8f5c617SMarkus Heiser        'type':     CObject,
164e8f5c617SMarkus Heiser        'var':      CObject,
165e8f5c617SMarkus Heiser    }
166