1e8f5c617SMarkus Heiser# -*- coding: utf-8; mode: python -*- 256cd8692SMarkus 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) 2956cd8692SMarkus Heiser 3056cd8692SMarkus Heiser * Handle signatures of function-like macros well. Don't try to deduce 3156cd8692SMarkus Heiser arguments types of function-like macros. 3256cd8692SMarkus Heiser 33e8f5c617SMarkus Heiser""" 34e8f5c617SMarkus Heiser 3556cd8692SMarkus Heiserfrom docutils import nodes 362c645cd7SMarkus Heiserfrom docutils.parsers.rst import directives 372c645cd7SMarkus Heiser 38b495360eSMarkus Heiserimport sphinx 3956cd8692SMarkus Heiserfrom sphinx import addnodes 4056cd8692SMarkus 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 4371e552aeSMauro Carvalho Chehabfrom itertools import chain 4471e552aeSMauro Carvalho Chehabimport re 45e8f5c617SMarkus Heiser 4671e552aeSMauro Carvalho Chehab__version__ = '1.1' 47e8f5c617SMarkus Heiser 48b495360eSMarkus Heiser# Get Sphinx version 49c46988aeSRémy Léonemajor, minor, patch = sphinx.version_info[:3] 50b495360eSMarkus Heiser 5171e552aeSMauro Carvalho Chehab# Namespace to be prepended to the full name 5271e552aeSMauro Carvalho Chehabnamespace = None 5371e552aeSMauro Carvalho Chehab 5471e552aeSMauro Carvalho Chehab# 5571e552aeSMauro Carvalho Chehab# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags 5671e552aeSMauro Carvalho Chehab# - Store the namespace if ".. c:namespace::" tag is found 5795f49490SMauro Carvalho Chehab# 5871e552aeSMauro Carvalho ChehabRE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') 5971e552aeSMauro Carvalho Chehab 6071e552aeSMauro Carvalho Chehabdef markup_namespace(match): 6171e552aeSMauro Carvalho Chehab global namespace 6271e552aeSMauro Carvalho Chehab 6371e552aeSMauro Carvalho Chehab namespace = match.group(1) 6471e552aeSMauro Carvalho Chehab 6571e552aeSMauro Carvalho Chehab return "" 6671e552aeSMauro Carvalho Chehab 6795f49490SMauro Carvalho Chehab# 6895f49490SMauro Carvalho Chehab# Handle c:macro for function-style declaration 6995f49490SMauro Carvalho Chehab# 7095f49490SMauro Carvalho ChehabRE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$') 7195f49490SMauro Carvalho Chehabdef markup_macro(match): 7295f49490SMauro Carvalho Chehab return ".. c:function:: " + match.group(1) + ' ' + match.group(2) 7395f49490SMauro Carvalho Chehab 7495f49490SMauro Carvalho Chehab# 7595f49490SMauro Carvalho Chehab# Handle newer c domain tags that are evaluated as .. c:type: for 7695f49490SMauro Carvalho Chehab# backward-compatibility with Sphinx < 3.0 7795f49490SMauro Carvalho Chehab# 7895f49490SMauro Carvalho ChehabRE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$') 7995f49490SMauro Carvalho Chehab 8095f49490SMauro Carvalho Chehabdef markup_ctype(match): 8195f49490SMauro Carvalho Chehab return ".. c:type:: " + match.group(2) 8295f49490SMauro Carvalho Chehab 8395f49490SMauro Carvalho Chehab# 8495f49490SMauro Carvalho Chehab# Handle newer c domain tags that are evaluated as :c:type: for 8595f49490SMauro Carvalho Chehab# backward-compatibility with Sphinx < 3.0 8695f49490SMauro Carvalho Chehab# 8795f49490SMauro Carvalho ChehabRE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`') 8895f49490SMauro Carvalho Chehabdef markup_ctype_refs(match): 8995f49490SMauro Carvalho Chehab return ":c:type:`" + match.group(2) + '`' 9095f49490SMauro Carvalho Chehab 9195f49490SMauro Carvalho Chehab# 9295f49490SMauro Carvalho Chehab# Simply convert :c:expr: and :c:texpr: into a literal block. 9395f49490SMauro Carvalho Chehab# 9495f49490SMauro Carvalho ChehabRE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`') 9595f49490SMauro Carvalho Chehabdef markup_c_expr(match): 9686a0adc0SBenjamin Gray return '\\ ``' + match.group(2) + '``\\ ' 9795f49490SMauro Carvalho Chehab 9895f49490SMauro Carvalho Chehab# 9995f49490SMauro Carvalho Chehab# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones 10095f49490SMauro Carvalho Chehab# 10171e552aeSMauro Carvalho Chehabdef c_markups(app, docname, source): 10271e552aeSMauro Carvalho Chehab result = "" 10371e552aeSMauro Carvalho Chehab markup_func = { 10471e552aeSMauro Carvalho Chehab RE_namespace: markup_namespace, 10595f49490SMauro Carvalho Chehab RE_expr: markup_c_expr, 10695f49490SMauro Carvalho Chehab RE_macro: markup_macro, 10795f49490SMauro Carvalho Chehab RE_ctype: markup_ctype, 10895f49490SMauro Carvalho Chehab RE_ctype_refs: markup_ctype_refs, 10971e552aeSMauro Carvalho Chehab } 11071e552aeSMauro Carvalho Chehab 11171e552aeSMauro Carvalho Chehab lines = iter(source[0].splitlines(True)) 11271e552aeSMauro Carvalho Chehab for n in lines: 11371e552aeSMauro Carvalho Chehab match_iterators = [regex.finditer(n) for regex in markup_func] 11471e552aeSMauro Carvalho Chehab matches = sorted(chain(*match_iterators), key=lambda m: m.start()) 11571e552aeSMauro Carvalho Chehab for m in matches: 11671e552aeSMauro Carvalho Chehab n = n[:m.start()] + markup_func[m.re](m) + n[m.end():] 11771e552aeSMauro Carvalho Chehab 11871e552aeSMauro Carvalho Chehab result = result + n 11971e552aeSMauro Carvalho Chehab 12071e552aeSMauro Carvalho Chehab source[0] = result 12171e552aeSMauro Carvalho Chehab 12271e552aeSMauro Carvalho Chehab# 12371e552aeSMauro Carvalho Chehab# Now implements support for the cdomain namespacing logic 12471e552aeSMauro Carvalho Chehab# 12571e552aeSMauro Carvalho Chehab 126e8f5c617SMarkus Heiserdef setup(app): 127e8f5c617SMarkus Heiser 12871e552aeSMauro Carvalho Chehab # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace:: 12971e552aeSMauro Carvalho Chehab app.connect('source-read', c_markups) 13042f6ebd8SMauro Carvalho Chehab app.add_domain(CDomain, override=True) 131e8f5c617SMarkus Heiser 132e8f5c617SMarkus Heiser return dict( 133e8f5c617SMarkus Heiser version = __version__, 134e8f5c617SMarkus Heiser parallel_read_safe = True, 135e8f5c617SMarkus Heiser parallel_write_safe = True 136e8f5c617SMarkus Heiser ) 137e8f5c617SMarkus Heiser 138e8f5c617SMarkus Heiserclass CObject(Base_CObject): 139e8f5c617SMarkus Heiser 140e8f5c617SMarkus Heiser """ 141e8f5c617SMarkus Heiser Description of a C language object. 142e8f5c617SMarkus Heiser """ 1432c645cd7SMarkus Heiser option_spec = { 1442c645cd7SMarkus Heiser "name" : directives.unchanged 1452c645cd7SMarkus Heiser } 1462c645cd7SMarkus Heiser 14756cd8692SMarkus Heiser def handle_func_like_macro(self, sig, signode): 14856cd8692SMarkus Heiser u"""Handles signatures of function-like macros. 14956cd8692SMarkus Heiser 150*60374870SCharles Han If the objtype is 'function' and the signature ``sig`` is a 15156cd8692SMarkus Heiser function-like macro, the name of the macro is returned. Otherwise 15256cd8692SMarkus Heiser ``False`` is returned. """ 15356cd8692SMarkus Heiser 15471e552aeSMauro Carvalho Chehab global namespace 15571e552aeSMauro Carvalho Chehab 15656cd8692SMarkus Heiser if not self.objtype == 'function': 15756cd8692SMarkus Heiser return False 15856cd8692SMarkus Heiser 15956cd8692SMarkus Heiser m = c_funcptr_sig_re.match(sig) 16056cd8692SMarkus Heiser if m is None: 16156cd8692SMarkus Heiser m = c_sig_re.match(sig) 16256cd8692SMarkus Heiser if m is None: 16356cd8692SMarkus Heiser raise ValueError('no match') 16456cd8692SMarkus Heiser 16556cd8692SMarkus Heiser rettype, fullname, arglist, _const = m.groups() 16656cd8692SMarkus Heiser arglist = arglist.strip() 16756cd8692SMarkus Heiser if rettype or not arglist: 16856cd8692SMarkus Heiser return False 16956cd8692SMarkus Heiser 17056cd8692SMarkus Heiser arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup 17156cd8692SMarkus Heiser arglist = [a.strip() for a in arglist.split(",")] 17256cd8692SMarkus Heiser 17356cd8692SMarkus Heiser # has the first argument a type? 17456cd8692SMarkus Heiser if len(arglist[0].split(" ")) > 1: 17556cd8692SMarkus Heiser return False 17656cd8692SMarkus Heiser 177d56b699dSBjorn Helgaas # This is a function-like macro, its arguments are typeless! 17856cd8692SMarkus Heiser signode += addnodes.desc_name(fullname, fullname) 17956cd8692SMarkus Heiser paramlist = addnodes.desc_parameterlist() 18056cd8692SMarkus Heiser signode += paramlist 18156cd8692SMarkus Heiser 18256cd8692SMarkus Heiser for argname in arglist: 18356cd8692SMarkus Heiser param = addnodes.desc_parameter('', '', noemph=True) 18456cd8692SMarkus Heiser # separate by non-breaking space in the output 18556cd8692SMarkus Heiser param += nodes.emphasis(argname, argname) 18656cd8692SMarkus Heiser paramlist += param 18756cd8692SMarkus Heiser 18871e552aeSMauro Carvalho Chehab if namespace: 18971e552aeSMauro Carvalho Chehab fullname = namespace + "." + fullname 19071e552aeSMauro Carvalho Chehab 19156cd8692SMarkus Heiser return fullname 19256cd8692SMarkus Heiser 1932c645cd7SMarkus Heiser def handle_signature(self, sig, signode): 1942c645cd7SMarkus Heiser """Transform a C signature into RST nodes.""" 19556cd8692SMarkus Heiser 19671e552aeSMauro Carvalho Chehab global namespace 19771e552aeSMauro Carvalho Chehab 19856cd8692SMarkus Heiser fullname = self.handle_func_like_macro(sig, signode) 19956cd8692SMarkus Heiser if not fullname: 2002c645cd7SMarkus Heiser fullname = super(CObject, self).handle_signature(sig, signode) 20156cd8692SMarkus Heiser 2022c645cd7SMarkus Heiser if "name" in self.options: 2032c645cd7SMarkus Heiser if self.objtype == 'function': 2042c645cd7SMarkus Heiser fullname = self.options["name"] 2052c645cd7SMarkus Heiser else: 2062c645cd7SMarkus Heiser # FIXME: handle :name: value of other declaration types? 2072c645cd7SMarkus Heiser pass 20871e552aeSMauro Carvalho Chehab else: 20971e552aeSMauro Carvalho Chehab if namespace: 21071e552aeSMauro Carvalho Chehab fullname = namespace + "." + fullname 21171e552aeSMauro Carvalho Chehab 2122c645cd7SMarkus Heiser return fullname 2132c645cd7SMarkus Heiser 214556aa6d5SMarkus Heiser def add_target_and_index(self, name, sig, signode): 215556aa6d5SMarkus Heiser # for C API items we add a prefix since names are usually not qualified 216556aa6d5SMarkus Heiser # by a module name and so easily clash with e.g. section titles 217556aa6d5SMarkus Heiser targetname = 'c.' + name 218556aa6d5SMarkus Heiser if targetname not in self.state.document.ids: 219556aa6d5SMarkus Heiser signode['names'].append(targetname) 220556aa6d5SMarkus Heiser signode['ids'].append(targetname) 221556aa6d5SMarkus Heiser signode['first'] = (not self.names) 222556aa6d5SMarkus Heiser self.state.document.note_explicit_target(signode) 223556aa6d5SMarkus Heiser inv = self.env.domaindata['c']['objects'] 224556aa6d5SMarkus Heiser if (name in inv and self.env.config.nitpicky): 225556aa6d5SMarkus Heiser if self.objtype == 'function': 226556aa6d5SMarkus Heiser if ('c:func', name) not in self.env.config.nitpick_ignore: 227556aa6d5SMarkus Heiser self.state_machine.reporter.warning( 228556aa6d5SMarkus Heiser 'duplicate C object description of %s, ' % name + 229556aa6d5SMarkus Heiser 'other instance in ' + self.env.doc2path(inv[name][0]), 230556aa6d5SMarkus Heiser line=self.lineno) 231556aa6d5SMarkus Heiser inv[name] = (self.env.docname, self.objtype) 232556aa6d5SMarkus Heiser 233556aa6d5SMarkus Heiser indextext = self.get_index_text(name) 234556aa6d5SMarkus Heiser if indextext: 235b495360eSMarkus Heiser self.indexnode['entries'].append( 236b495360eSMarkus Heiser ('single', indextext, targetname, '', None)) 237e8f5c617SMarkus Heiser 238e8f5c617SMarkus Heiserclass CDomain(Base_CDomain): 239e8f5c617SMarkus Heiser 240e8f5c617SMarkus Heiser """C language domain.""" 241e8f5c617SMarkus Heiser name = 'c' 242e8f5c617SMarkus Heiser label = 'C' 243e8f5c617SMarkus Heiser directives = { 244e8f5c617SMarkus Heiser 'function': CObject, 245e8f5c617SMarkus Heiser 'member': CObject, 246e8f5c617SMarkus Heiser 'macro': CObject, 247e8f5c617SMarkus Heiser 'type': CObject, 248e8f5c617SMarkus Heiser 'var': CObject, 249e8f5c617SMarkus Heiser } 250