18c2ecf20Sopenharmony_ci# -*- coding: utf-8; mode: python -*- 28c2ecf20Sopenharmony_ci# pylint: disable=W0141,C0113,C0103,C0325 38c2ecf20Sopenharmony_ciu""" 48c2ecf20Sopenharmony_ci cdomain 58c2ecf20Sopenharmony_ci ~~~~~~~ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Replacement for the sphinx c-domain. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci :copyright: Copyright (C) 2016 Markus Heiser 108c2ecf20Sopenharmony_ci :license: GPL Version 2, June 1991 see Linux/COPYING for details. 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci List of customizations: 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci * Moved the *duplicate C object description* warnings for function 158c2ecf20Sopenharmony_ci declarations in the nitpicky mode. See Sphinx documentation for 168c2ecf20Sopenharmony_ci the config values for ``nitpick`` and ``nitpick_ignore``. 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci * Add option 'name' to the "c:function:" directive. With option 'name' the 198c2ecf20Sopenharmony_ci ref-name of a function can be modified. E.g.:: 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci .. c:function:: int ioctl( int fd, int request ) 228c2ecf20Sopenharmony_ci :name: VIDIOC_LOG_STATUS 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci The func-name (e.g. ioctl) remains in the output but the ref-name changed 258c2ecf20Sopenharmony_ci from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by:: 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci * :c:func:`VIDIOC_LOG_STATUS` or 288c2ecf20Sopenharmony_ci * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci * Handle signatures of function-like macros well. Don't try to deduce 318c2ecf20Sopenharmony_ci arguments types of function-like macros. 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci""" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cifrom docutils import nodes 368c2ecf20Sopenharmony_cifrom docutils.parsers.rst import directives 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciimport sphinx 398c2ecf20Sopenharmony_cifrom sphinx import addnodes 408c2ecf20Sopenharmony_cifrom sphinx.domains.c import c_funcptr_sig_re, c_sig_re 418c2ecf20Sopenharmony_cifrom sphinx.domains.c import CObject as Base_CObject 428c2ecf20Sopenharmony_cifrom sphinx.domains.c import CDomain as Base_CDomain 438c2ecf20Sopenharmony_cifrom itertools import chain 448c2ecf20Sopenharmony_ciimport re 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci__version__ = '1.1' 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci# Get Sphinx version 498c2ecf20Sopenharmony_cimajor, minor, patch = sphinx.version_info[:3] 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci# Namespace to be prepended to the full name 528c2ecf20Sopenharmony_cinamespace = None 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci# 558c2ecf20Sopenharmony_ci# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags 568c2ecf20Sopenharmony_ci# - Store the namespace if ".. c:namespace::" tag is found 578c2ecf20Sopenharmony_ci# 588c2ecf20Sopenharmony_ciRE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cidef markup_namespace(match): 618c2ecf20Sopenharmony_ci global namespace 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci namespace = match.group(1) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return "" 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci# 688c2ecf20Sopenharmony_ci# Handle c:macro for function-style declaration 698c2ecf20Sopenharmony_ci# 708c2ecf20Sopenharmony_ciRE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$') 718c2ecf20Sopenharmony_cidef markup_macro(match): 728c2ecf20Sopenharmony_ci return ".. c:function:: " + match.group(1) + ' ' + match.group(2) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci# 758c2ecf20Sopenharmony_ci# Handle newer c domain tags that are evaluated as .. c:type: for 768c2ecf20Sopenharmony_ci# backward-compatibility with Sphinx < 3.0 778c2ecf20Sopenharmony_ci# 788c2ecf20Sopenharmony_ciRE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$') 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cidef markup_ctype(match): 818c2ecf20Sopenharmony_ci return ".. c:type:: " + match.group(2) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci# 848c2ecf20Sopenharmony_ci# Handle newer c domain tags that are evaluated as :c:type: for 858c2ecf20Sopenharmony_ci# backward-compatibility with Sphinx < 3.0 868c2ecf20Sopenharmony_ci# 878c2ecf20Sopenharmony_ciRE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`') 888c2ecf20Sopenharmony_cidef markup_ctype_refs(match): 898c2ecf20Sopenharmony_ci return ":c:type:`" + match.group(2) + '`' 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci# 928c2ecf20Sopenharmony_ci# Simply convert :c:expr: and :c:texpr: into a literal block. 938c2ecf20Sopenharmony_ci# 948c2ecf20Sopenharmony_ciRE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`') 958c2ecf20Sopenharmony_cidef markup_c_expr(match): 968c2ecf20Sopenharmony_ci return '\ ``' + match.group(2) + '``\ ' 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci# 998c2ecf20Sopenharmony_ci# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones 1008c2ecf20Sopenharmony_ci# 1018c2ecf20Sopenharmony_cidef c_markups(app, docname, source): 1028c2ecf20Sopenharmony_ci result = "" 1038c2ecf20Sopenharmony_ci markup_func = { 1048c2ecf20Sopenharmony_ci RE_namespace: markup_namespace, 1058c2ecf20Sopenharmony_ci RE_expr: markup_c_expr, 1068c2ecf20Sopenharmony_ci RE_macro: markup_macro, 1078c2ecf20Sopenharmony_ci RE_ctype: markup_ctype, 1088c2ecf20Sopenharmony_ci RE_ctype_refs: markup_ctype_refs, 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci lines = iter(source[0].splitlines(True)) 1128c2ecf20Sopenharmony_ci for n in lines: 1138c2ecf20Sopenharmony_ci match_iterators = [regex.finditer(n) for regex in markup_func] 1148c2ecf20Sopenharmony_ci matches = sorted(chain(*match_iterators), key=lambda m: m.start()) 1158c2ecf20Sopenharmony_ci for m in matches: 1168c2ecf20Sopenharmony_ci n = n[:m.start()] + markup_func[m.re](m) + n[m.end():] 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci result = result + n 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci source[0] = result 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci# 1238c2ecf20Sopenharmony_ci# Now implements support for the cdomain namespacing logic 1248c2ecf20Sopenharmony_ci# 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cidef setup(app): 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace:: 1298c2ecf20Sopenharmony_ci app.connect('source-read', c_markups) 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (major == 1 and minor < 8): 1328c2ecf20Sopenharmony_ci app.override_domain(CDomain) 1338c2ecf20Sopenharmony_ci else: 1348c2ecf20Sopenharmony_ci app.add_domain(CDomain, override=True) 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return dict( 1378c2ecf20Sopenharmony_ci version = __version__, 1388c2ecf20Sopenharmony_ci parallel_read_safe = True, 1398c2ecf20Sopenharmony_ci parallel_write_safe = True 1408c2ecf20Sopenharmony_ci ) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ciclass CObject(Base_CObject): 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci """ 1458c2ecf20Sopenharmony_ci Description of a C language object. 1468c2ecf20Sopenharmony_ci """ 1478c2ecf20Sopenharmony_ci option_spec = { 1488c2ecf20Sopenharmony_ci "name" : directives.unchanged 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci def handle_func_like_macro(self, sig, signode): 1528c2ecf20Sopenharmony_ci u"""Handles signatures of function-like macros. 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci If the objtype is 'function' and the the signature ``sig`` is a 1558c2ecf20Sopenharmony_ci function-like macro, the name of the macro is returned. Otherwise 1568c2ecf20Sopenharmony_ci ``False`` is returned. """ 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci global namespace 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if not self.objtype == 'function': 1618c2ecf20Sopenharmony_ci return False 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci m = c_funcptr_sig_re.match(sig) 1648c2ecf20Sopenharmony_ci if m is None: 1658c2ecf20Sopenharmony_ci m = c_sig_re.match(sig) 1668c2ecf20Sopenharmony_ci if m is None: 1678c2ecf20Sopenharmony_ci raise ValueError('no match') 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci rettype, fullname, arglist, _const = m.groups() 1708c2ecf20Sopenharmony_ci arglist = arglist.strip() 1718c2ecf20Sopenharmony_ci if rettype or not arglist: 1728c2ecf20Sopenharmony_ci return False 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup 1758c2ecf20Sopenharmony_ci arglist = [a.strip() for a in arglist.split(",")] 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci # has the first argument a type? 1788c2ecf20Sopenharmony_ci if len(arglist[0].split(" ")) > 1: 1798c2ecf20Sopenharmony_ci return False 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci # This is a function-like macro, it's arguments are typeless! 1828c2ecf20Sopenharmony_ci signode += addnodes.desc_name(fullname, fullname) 1838c2ecf20Sopenharmony_ci paramlist = addnodes.desc_parameterlist() 1848c2ecf20Sopenharmony_ci signode += paramlist 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci for argname in arglist: 1878c2ecf20Sopenharmony_ci param = addnodes.desc_parameter('', '', noemph=True) 1888c2ecf20Sopenharmony_ci # separate by non-breaking space in the output 1898c2ecf20Sopenharmony_ci param += nodes.emphasis(argname, argname) 1908c2ecf20Sopenharmony_ci paramlist += param 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if namespace: 1938c2ecf20Sopenharmony_ci fullname = namespace + "." + fullname 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return fullname 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci def handle_signature(self, sig, signode): 1988c2ecf20Sopenharmony_ci """Transform a C signature into RST nodes.""" 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci global namespace 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci fullname = self.handle_func_like_macro(sig, signode) 2038c2ecf20Sopenharmony_ci if not fullname: 2048c2ecf20Sopenharmony_ci fullname = super(CObject, self).handle_signature(sig, signode) 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if "name" in self.options: 2078c2ecf20Sopenharmony_ci if self.objtype == 'function': 2088c2ecf20Sopenharmony_ci fullname = self.options["name"] 2098c2ecf20Sopenharmony_ci else: 2108c2ecf20Sopenharmony_ci # FIXME: handle :name: value of other declaration types? 2118c2ecf20Sopenharmony_ci pass 2128c2ecf20Sopenharmony_ci else: 2138c2ecf20Sopenharmony_ci if namespace: 2148c2ecf20Sopenharmony_ci fullname = namespace + "." + fullname 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return fullname 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci def add_target_and_index(self, name, sig, signode): 2198c2ecf20Sopenharmony_ci # for C API items we add a prefix since names are usually not qualified 2208c2ecf20Sopenharmony_ci # by a module name and so easily clash with e.g. section titles 2218c2ecf20Sopenharmony_ci targetname = 'c.' + name 2228c2ecf20Sopenharmony_ci if targetname not in self.state.document.ids: 2238c2ecf20Sopenharmony_ci signode['names'].append(targetname) 2248c2ecf20Sopenharmony_ci signode['ids'].append(targetname) 2258c2ecf20Sopenharmony_ci signode['first'] = (not self.names) 2268c2ecf20Sopenharmony_ci self.state.document.note_explicit_target(signode) 2278c2ecf20Sopenharmony_ci inv = self.env.domaindata['c']['objects'] 2288c2ecf20Sopenharmony_ci if (name in inv and self.env.config.nitpicky): 2298c2ecf20Sopenharmony_ci if self.objtype == 'function': 2308c2ecf20Sopenharmony_ci if ('c:func', name) not in self.env.config.nitpick_ignore: 2318c2ecf20Sopenharmony_ci self.state_machine.reporter.warning( 2328c2ecf20Sopenharmony_ci 'duplicate C object description of %s, ' % name + 2338c2ecf20Sopenharmony_ci 'other instance in ' + self.env.doc2path(inv[name][0]), 2348c2ecf20Sopenharmony_ci line=self.lineno) 2358c2ecf20Sopenharmony_ci inv[name] = (self.env.docname, self.objtype) 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci indextext = self.get_index_text(name) 2388c2ecf20Sopenharmony_ci if indextext: 2398c2ecf20Sopenharmony_ci if major == 1 and minor < 4: 2408c2ecf20Sopenharmony_ci # indexnode's tuple changed in 1.4 2418c2ecf20Sopenharmony_ci # https://github.com/sphinx-doc/sphinx/commit/e6a5a3a92e938fcd75866b4227db9e0524d58f7c 2428c2ecf20Sopenharmony_ci self.indexnode['entries'].append( 2438c2ecf20Sopenharmony_ci ('single', indextext, targetname, '')) 2448c2ecf20Sopenharmony_ci else: 2458c2ecf20Sopenharmony_ci self.indexnode['entries'].append( 2468c2ecf20Sopenharmony_ci ('single', indextext, targetname, '', None)) 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ciclass CDomain(Base_CDomain): 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci """C language domain.""" 2518c2ecf20Sopenharmony_ci name = 'c' 2528c2ecf20Sopenharmony_ci label = 'C' 2538c2ecf20Sopenharmony_ci directives = { 2548c2ecf20Sopenharmony_ci 'function': CObject, 2558c2ecf20Sopenharmony_ci 'member': CObject, 2568c2ecf20Sopenharmony_ci 'macro': CObject, 2578c2ecf20Sopenharmony_ci 'type': CObject, 2588c2ecf20Sopenharmony_ci 'var': CObject, 2598c2ecf20Sopenharmony_ci } 260