0001
0002
0003 u"""
0004 cdomain
0005 ~~~~~~~
0006
0007 Replacement for the sphinx c-domain.
0008
0009 :copyright: Copyright (C) 2016 Markus Heiser
0010 :license: GPL Version 2, June 1991 see Linux/COPYING for details.
0011
0012 List of customizations:
0013
0014 * Moved the *duplicate C object description* warnings for function
0015 declarations in the nitpicky mode. See Sphinx documentation for
0016 the config values for ``nitpick`` and ``nitpick_ignore``.
0017
0018 * Add option 'name' to the "c:function:" directive. With option 'name' the
0019 ref-name of a function can be modified. E.g.::
0020
0021 .. c:function:: int ioctl( int fd, int request )
0022 :name: VIDIOC_LOG_STATUS
0023
0024 The func-name (e.g. ioctl) remains in the output but the ref-name changed
0025 from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by::
0026
0027 * :c:func:`VIDIOC_LOG_STATUS` or
0028 * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3)
0029
0030 * Handle signatures of function-like macros well. Don't try to deduce
0031 arguments types of function-like macros.
0032
0033 """
0034
0035 from docutils import nodes
0036 from docutils.parsers.rst import directives
0037
0038 import sphinx
0039 from sphinx import addnodes
0040 from sphinx.domains.c import c_funcptr_sig_re, c_sig_re
0041 from sphinx.domains.c import CObject as Base_CObject
0042 from sphinx.domains.c import CDomain as Base_CDomain
0043 from itertools import chain
0044 import re
0045
0046 __version__ = '1.1'
0047
0048
0049 major, minor, patch = sphinx.version_info[:3]
0050
0051
0052 namespace = None
0053
0054
0055
0056
0057
0058 RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$')
0059
0060 def markup_namespace(match):
0061 global namespace
0062
0063 namespace = match.group(1)
0064
0065 return ""
0066
0067
0068
0069
0070 RE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$')
0071 def markup_macro(match):
0072 return ".. c:function:: " + match.group(1) + ' ' + match.group(2)
0073
0074
0075
0076
0077
0078 RE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$')
0079
0080 def markup_ctype(match):
0081 return ".. c:type:: " + match.group(2)
0082
0083
0084
0085
0086
0087 RE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`')
0088 def markup_ctype_refs(match):
0089 return ":c:type:`" + match.group(2) + '`'
0090
0091
0092
0093
0094 RE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`')
0095 def markup_c_expr(match):
0096 return '\ ``' + match.group(2) + '``\ '
0097
0098
0099
0100
0101 def c_markups(app, docname, source):
0102 result = ""
0103 markup_func = {
0104 RE_namespace: markup_namespace,
0105 RE_expr: markup_c_expr,
0106 RE_macro: markup_macro,
0107 RE_ctype: markup_ctype,
0108 RE_ctype_refs: markup_ctype_refs,
0109 }
0110
0111 lines = iter(source[0].splitlines(True))
0112 for n in lines:
0113 match_iterators = [regex.finditer(n) for regex in markup_func]
0114 matches = sorted(chain(*match_iterators), key=lambda m: m.start())
0115 for m in matches:
0116 n = n[:m.start()] + markup_func[m.re](m) + n[m.end():]
0117
0118 result = result + n
0119
0120 source[0] = result
0121
0122
0123
0124
0125
0126 def setup(app):
0127
0128
0129 app.connect('source-read', c_markups)
0130
0131 if (major == 1 and minor < 8):
0132 app.override_domain(CDomain)
0133 else:
0134 app.add_domain(CDomain, override=True)
0135
0136 return dict(
0137 version = __version__,
0138 parallel_read_safe = True,
0139 parallel_write_safe = True
0140 )
0141
0142 class CObject(Base_CObject):
0143
0144 """
0145 Description of a C language object.
0146 """
0147 option_spec = {
0148 "name" : directives.unchanged
0149 }
0150
0151 def handle_func_like_macro(self, sig, signode):
0152 u"""Handles signatures of function-like macros.
0153
0154 If the objtype is 'function' and the the signature ``sig`` is a
0155 function-like macro, the name of the macro is returned. Otherwise
0156 ``False`` is returned. """
0157
0158 global namespace
0159
0160 if not self.objtype == 'function':
0161 return False
0162
0163 m = c_funcptr_sig_re.match(sig)
0164 if m is None:
0165 m = c_sig_re.match(sig)
0166 if m is None:
0167 raise ValueError('no match')
0168
0169 rettype, fullname, arglist, _const = m.groups()
0170 arglist = arglist.strip()
0171 if rettype or not arglist:
0172 return False
0173
0174 arglist = arglist.replace('`', '').replace('\\ ', '')
0175 arglist = [a.strip() for a in arglist.split(",")]
0176
0177
0178 if len(arglist[0].split(" ")) > 1:
0179 return False
0180
0181
0182 signode += addnodes.desc_name(fullname, fullname)
0183 paramlist = addnodes.desc_parameterlist()
0184 signode += paramlist
0185
0186 for argname in arglist:
0187 param = addnodes.desc_parameter('', '', noemph=True)
0188
0189 param += nodes.emphasis(argname, argname)
0190 paramlist += param
0191
0192 if namespace:
0193 fullname = namespace + "." + fullname
0194
0195 return fullname
0196
0197 def handle_signature(self, sig, signode):
0198 """Transform a C signature into RST nodes."""
0199
0200 global namespace
0201
0202 fullname = self.handle_func_like_macro(sig, signode)
0203 if not fullname:
0204 fullname = super(CObject, self).handle_signature(sig, signode)
0205
0206 if "name" in self.options:
0207 if self.objtype == 'function':
0208 fullname = self.options["name"]
0209 else:
0210
0211 pass
0212 else:
0213 if namespace:
0214 fullname = namespace + "." + fullname
0215
0216 return fullname
0217
0218 def add_target_and_index(self, name, sig, signode):
0219
0220
0221 targetname = 'c.' + name
0222 if targetname not in self.state.document.ids:
0223 signode['names'].append(targetname)
0224 signode['ids'].append(targetname)
0225 signode['first'] = (not self.names)
0226 self.state.document.note_explicit_target(signode)
0227 inv = self.env.domaindata['c']['objects']
0228 if (name in inv and self.env.config.nitpicky):
0229 if self.objtype == 'function':
0230 if ('c:func', name) not in self.env.config.nitpick_ignore:
0231 self.state_machine.reporter.warning(
0232 'duplicate C object description of %s, ' % name +
0233 'other instance in ' + self.env.doc2path(inv[name][0]),
0234 line=self.lineno)
0235 inv[name] = (self.env.docname, self.objtype)
0236
0237 indextext = self.get_index_text(name)
0238 if indextext:
0239 self.indexnode['entries'].append(
0240 ('single', indextext, targetname, '', None))
0241
0242 class CDomain(Base_CDomain):
0243
0244 """C language domain."""
0245 name = 'c'
0246 label = 'C'
0247 directives = {
0248 'function': CObject,
0249 'member': CObject,
0250 'macro': CObject,
0251 'type': CObject,
0252 'var': CObject,
0253 }