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