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 >> 131 if (major == 1 and minor < 8): >> 132 app.override_domain(CDomain) >> 133 else: >> 134 app.add_domain(CDomain, override=True) 131 135 132 return dict( 136 return dict( 133 version = __version__, 137 version = __version__, 134 parallel_read_safe = True, 138 parallel_read_safe = True, 135 parallel_write_safe = True 139 parallel_write_safe = True 136 ) 140 ) 137 141 138 class CObject(Base_CObject): 142 class CObject(Base_CObject): 139 143 140 """ 144 """ 141 Description of a C language object. 145 Description of a C language object. 142 """ 146 """ 143 option_spec = { 147 option_spec = { 144 "name" : directives.unchanged 148 "name" : directives.unchanged 145 } 149 } 146 150 147 def handle_func_like_macro(self, sig, sign 151 def handle_func_like_macro(self, sig, signode): 148 u"""Handles signatures of function-lik 152 u"""Handles signatures of function-like macros. 149 153 150 If the objtype is 'function' and the s 154 If the objtype is 'function' and the signature ``sig`` is a 151 function-like macro, the name of the m 155 function-like macro, the name of the macro is returned. Otherwise 152 ``False`` is returned. """ 156 ``False`` is returned. """ 153 157 154 global namespace 158 global namespace 155 159 156 if not self.objtype == 'function': 160 if not self.objtype == 'function': 157 return False 161 return False 158 162 159 m = c_funcptr_sig_re.match(sig) 163 m = c_funcptr_sig_re.match(sig) 160 if m is None: 164 if m is None: 161 m = c_sig_re.match(sig) 165 m = c_sig_re.match(sig) 162 if m is None: 166 if m is None: 163 raise ValueError('no match') 167 raise ValueError('no match') 164 168 165 rettype, fullname, arglist, _const = m 169 rettype, fullname, arglist, _const = m.groups() 166 arglist = arglist.strip() 170 arglist = arglist.strip() 167 if rettype or not arglist: 171 if rettype or not arglist: 168 return False 172 return False 169 173 170 arglist = arglist.replace('`', '').rep 174 arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup 171 arglist = [a.strip() for a in arglist. 175 arglist = [a.strip() for a in arglist.split(",")] 172 176 173 # has the first argument a type? 177 # has the first argument a type? 174 if len(arglist[0].split(" ")) > 1: 178 if len(arglist[0].split(" ")) > 1: 175 return False 179 return False 176 180 177 # This is a function-like macro, its a 181 # This is a function-like macro, its arguments are typeless! 178 signode += addnodes.desc_name(fullnam 182 signode += addnodes.desc_name(fullname, fullname) 179 paramlist = addnodes.desc_parameterlis 183 paramlist = addnodes.desc_parameterlist() 180 signode += paramlist 184 signode += paramlist 181 185 182 for argname in arglist: 186 for argname in arglist: 183 param = addnodes.desc_parameter('' 187 param = addnodes.desc_parameter('', '', noemph=True) 184 # separate by non-breaking space i 188 # separate by non-breaking space in the output 185 param += nodes.emphasis(argname, a 189 param += nodes.emphasis(argname, argname) 186 paramlist += param 190 paramlist += param 187 191 188 if namespace: 192 if namespace: 189 fullname = namespace + "." + fulln 193 fullname = namespace + "." + fullname 190 194 191 return fullname 195 return fullname 192 196 193 def handle_signature(self, sig, signode): 197 def handle_signature(self, sig, signode): 194 """Transform a C signature into RST no 198 """Transform a C signature into RST nodes.""" 195 199 196 global namespace 200 global namespace 197 201 198 fullname = self.handle_func_like_macro 202 fullname = self.handle_func_like_macro(sig, signode) 199 if not fullname: 203 if not fullname: 200 fullname = super(CObject, self).ha 204 fullname = super(CObject, self).handle_signature(sig, signode) 201 205 202 if "name" in self.options: 206 if "name" in self.options: 203 if self.objtype == 'function': 207 if self.objtype == 'function': 204 fullname = self.options["name" 208 fullname = self.options["name"] 205 else: 209 else: 206 # FIXME: handle :name: value o 210 # FIXME: handle :name: value of other declaration types? 207 pass 211 pass 208 else: 212 else: 209 if namespace: 213 if namespace: 210 fullname = namespace + "." + f 214 fullname = namespace + "." + fullname 211 215 212 return fullname 216 return fullname 213 217 214 def add_target_and_index(self, name, sig, 218 def add_target_and_index(self, name, sig, signode): 215 # for C API items we add a prefix sinc 219 # for C API items we add a prefix since names are usually not qualified 216 # by a module name and so easily clash 220 # by a module name and so easily clash with e.g. section titles 217 targetname = 'c.' + name 221 targetname = 'c.' + name 218 if targetname not in self.state.docume 222 if targetname not in self.state.document.ids: 219 signode['names'].append(targetname 223 signode['names'].append(targetname) 220 signode['ids'].append(targetname) 224 signode['ids'].append(targetname) 221 signode['first'] = (not self.names 225 signode['first'] = (not self.names) 222 self.state.document.note_explicit_ 226 self.state.document.note_explicit_target(signode) 223 inv = self.env.domaindata['c']['ob 227 inv = self.env.domaindata['c']['objects'] 224 if (name in inv and self.env.confi 228 if (name in inv and self.env.config.nitpicky): 225 if self.objtype == 'function': 229 if self.objtype == 'function': 226 if ('c:func', name) not in 230 if ('c:func', name) not in self.env.config.nitpick_ignore: 227 self.state_machine.rep 231 self.state_machine.reporter.warning( 228 'duplicate C objec 232 'duplicate C object description of %s, ' % name + 229 'other instance in 233 'other instance in ' + self.env.doc2path(inv[name][0]), 230 line=self.lineno) 234 line=self.lineno) 231 inv[name] = (self.env.docname, sel 235 inv[name] = (self.env.docname, self.objtype) 232 236 233 indextext = self.get_index_text(name) 237 indextext = self.get_index_text(name) 234 if indextext: 238 if indextext: 235 self.indexnode['entries'].append( 239 self.indexnode['entries'].append( 236 ('single', indextext, targ 240 ('single', indextext, targetname, '', None)) 237 241 238 class CDomain(Base_CDomain): 242 class CDomain(Base_CDomain): 239 243 240 """C language domain.""" 244 """C language domain.""" 241 name = 'c' 245 name = 'c' 242 label = 'C' 246 label = 'C' 243 directives = { 247 directives = { 244 'function': CObject, 248 'function': CObject, 245 'member': CObject, 249 'member': CObject, 246 'macro': CObject, 250 'macro': CObject, 247 'type': CObject, 251 'type': CObject, 248 'var': CObject, 252 'var': CObject, 249 } 253 }
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.