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 43*71e552aeSMauro Carvalho Chehabfrom itertools import chain 44*71e552aeSMauro Carvalho Chehabimport re 45e8f5c617SMarkus Heiser 46*71e552aeSMauro Carvalho Chehab__version__ = '1.1' 47e8f5c617SMarkus Heiser 48b495360eSMarkus Heiser# Get Sphinx version 49c46988aeSRémy Léonemajor, minor, patch = sphinx.version_info[:3] 50b495360eSMarkus Heiser 51*71e552aeSMauro Carvalho Chehab# Namespace to be prepended to the full name 52*71e552aeSMauro Carvalho Chehabnamespace = None 53*71e552aeSMauro Carvalho Chehab 54*71e552aeSMauro Carvalho Chehab# 55*71e552aeSMauro Carvalho Chehab# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags 56*71e552aeSMauro Carvalho Chehab# - Store the namespace if ".. c:namespace::" tag is found 57*71e552aeSMauro Carvalho Chehab 58*71e552aeSMauro Carvalho ChehabRE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') 59*71e552aeSMauro Carvalho Chehab 60*71e552aeSMauro Carvalho Chehabdef markup_namespace(match): 61*71e552aeSMauro Carvalho Chehab global namespace 62*71e552aeSMauro Carvalho Chehab 63*71e552aeSMauro Carvalho Chehab namespace = match.group(1) 64*71e552aeSMauro Carvalho Chehab 65*71e552aeSMauro Carvalho Chehab return "" 66*71e552aeSMauro Carvalho Chehab 67*71e552aeSMauro Carvalho Chehabdef c_markups(app, docname, source): 68*71e552aeSMauro Carvalho Chehab result = "" 69*71e552aeSMauro Carvalho Chehab markup_func = { 70*71e552aeSMauro Carvalho Chehab RE_namespace: markup_namespace, 71*71e552aeSMauro Carvalho Chehab } 72*71e552aeSMauro Carvalho Chehab 73*71e552aeSMauro Carvalho Chehab lines = iter(source[0].splitlines(True)) 74*71e552aeSMauro Carvalho Chehab for n in lines: 75*71e552aeSMauro Carvalho Chehab match_iterators = [regex.finditer(n) for regex in markup_func] 76*71e552aeSMauro Carvalho Chehab matches = sorted(chain(*match_iterators), key=lambda m: m.start()) 77*71e552aeSMauro Carvalho Chehab for m in matches: 78*71e552aeSMauro Carvalho Chehab n = n[:m.start()] + markup_func[m.re](m) + n[m.end():] 79*71e552aeSMauro Carvalho Chehab 80*71e552aeSMauro Carvalho Chehab result = result + n 81*71e552aeSMauro Carvalho Chehab 82*71e552aeSMauro Carvalho Chehab source[0] = result 83*71e552aeSMauro Carvalho Chehab 84*71e552aeSMauro Carvalho Chehab# 85*71e552aeSMauro Carvalho Chehab# Now implements support for the cdomain namespacing logic 86*71e552aeSMauro Carvalho Chehab# 87*71e552aeSMauro Carvalho Chehab 88e8f5c617SMarkus Heiserdef setup(app): 89e8f5c617SMarkus Heiser 90*71e552aeSMauro Carvalho Chehab # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace:: 91*71e552aeSMauro Carvalho Chehab app.connect('source-read', c_markups) 92*71e552aeSMauro Carvalho Chehab 9342f6ebd8SMauro Carvalho Chehab if (major == 1 and minor < 8): 94e8f5c617SMarkus Heiser app.override_domain(CDomain) 9542f6ebd8SMauro Carvalho Chehab else: 9642f6ebd8SMauro Carvalho Chehab app.add_domain(CDomain, override=True) 97e8f5c617SMarkus Heiser 98e8f5c617SMarkus Heiser return dict( 99e8f5c617SMarkus Heiser version = __version__, 100e8f5c617SMarkus Heiser parallel_read_safe = True, 101e8f5c617SMarkus Heiser parallel_write_safe = True 102e8f5c617SMarkus Heiser ) 103e8f5c617SMarkus Heiser 104e8f5c617SMarkus Heiserclass CObject(Base_CObject): 105e8f5c617SMarkus Heiser 106e8f5c617SMarkus Heiser """ 107e8f5c617SMarkus Heiser Description of a C language object. 108e8f5c617SMarkus Heiser """ 1092c645cd7SMarkus Heiser option_spec = { 1102c645cd7SMarkus Heiser "name" : directives.unchanged 1112c645cd7SMarkus Heiser } 1122c645cd7SMarkus Heiser 11356cd8692SMarkus Heiser def handle_func_like_macro(self, sig, signode): 11456cd8692SMarkus Heiser u"""Handles signatures of function-like macros. 11556cd8692SMarkus Heiser 11656cd8692SMarkus Heiser If the objtype is 'function' and the the signature ``sig`` is a 11756cd8692SMarkus Heiser function-like macro, the name of the macro is returned. Otherwise 11856cd8692SMarkus Heiser ``False`` is returned. """ 11956cd8692SMarkus Heiser 120*71e552aeSMauro Carvalho Chehab global namespace 121*71e552aeSMauro Carvalho Chehab 12256cd8692SMarkus Heiser if not self.objtype == 'function': 12356cd8692SMarkus Heiser return False 12456cd8692SMarkus Heiser 12556cd8692SMarkus Heiser m = c_funcptr_sig_re.match(sig) 12656cd8692SMarkus Heiser if m is None: 12756cd8692SMarkus Heiser m = c_sig_re.match(sig) 12856cd8692SMarkus Heiser if m is None: 12956cd8692SMarkus Heiser raise ValueError('no match') 13056cd8692SMarkus Heiser 13156cd8692SMarkus Heiser rettype, fullname, arglist, _const = m.groups() 13256cd8692SMarkus Heiser arglist = arglist.strip() 13356cd8692SMarkus Heiser if rettype or not arglist: 13456cd8692SMarkus Heiser return False 13556cd8692SMarkus Heiser 13656cd8692SMarkus Heiser arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup 13756cd8692SMarkus Heiser arglist = [a.strip() for a in arglist.split(",")] 13856cd8692SMarkus Heiser 13956cd8692SMarkus Heiser # has the first argument a type? 14056cd8692SMarkus Heiser if len(arglist[0].split(" ")) > 1: 14156cd8692SMarkus Heiser return False 14256cd8692SMarkus Heiser 14356cd8692SMarkus Heiser # This is a function-like macro, it's arguments are typeless! 14456cd8692SMarkus Heiser signode += addnodes.desc_name(fullname, fullname) 14556cd8692SMarkus Heiser paramlist = addnodes.desc_parameterlist() 14656cd8692SMarkus Heiser signode += paramlist 14756cd8692SMarkus Heiser 14856cd8692SMarkus Heiser for argname in arglist: 14956cd8692SMarkus Heiser param = addnodes.desc_parameter('', '', noemph=True) 15056cd8692SMarkus Heiser # separate by non-breaking space in the output 15156cd8692SMarkus Heiser param += nodes.emphasis(argname, argname) 15256cd8692SMarkus Heiser paramlist += param 15356cd8692SMarkus Heiser 154*71e552aeSMauro Carvalho Chehab if namespace: 155*71e552aeSMauro Carvalho Chehab fullname = namespace + "." + fullname 156*71e552aeSMauro Carvalho Chehab 15756cd8692SMarkus Heiser return fullname 15856cd8692SMarkus Heiser 1592c645cd7SMarkus Heiser def handle_signature(self, sig, signode): 1602c645cd7SMarkus Heiser """Transform a C signature into RST nodes.""" 16156cd8692SMarkus Heiser 162*71e552aeSMauro Carvalho Chehab global namespace 163*71e552aeSMauro Carvalho Chehab 16456cd8692SMarkus Heiser fullname = self.handle_func_like_macro(sig, signode) 16556cd8692SMarkus Heiser if not fullname: 1662c645cd7SMarkus Heiser fullname = super(CObject, self).handle_signature(sig, signode) 16756cd8692SMarkus Heiser 1682c645cd7SMarkus Heiser if "name" in self.options: 1692c645cd7SMarkus Heiser if self.objtype == 'function': 1702c645cd7SMarkus Heiser fullname = self.options["name"] 1712c645cd7SMarkus Heiser else: 1722c645cd7SMarkus Heiser # FIXME: handle :name: value of other declaration types? 1732c645cd7SMarkus Heiser pass 174*71e552aeSMauro Carvalho Chehab else: 175*71e552aeSMauro Carvalho Chehab if namespace: 176*71e552aeSMauro Carvalho Chehab fullname = namespace + "." + fullname 177*71e552aeSMauro Carvalho Chehab 1782c645cd7SMarkus Heiser return fullname 1792c645cd7SMarkus Heiser 180556aa6d5SMarkus Heiser def add_target_and_index(self, name, sig, signode): 181556aa6d5SMarkus Heiser # for C API items we add a prefix since names are usually not qualified 182556aa6d5SMarkus Heiser # by a module name and so easily clash with e.g. section titles 183556aa6d5SMarkus Heiser targetname = 'c.' + name 184556aa6d5SMarkus Heiser if targetname not in self.state.document.ids: 185556aa6d5SMarkus Heiser signode['names'].append(targetname) 186556aa6d5SMarkus Heiser signode['ids'].append(targetname) 187556aa6d5SMarkus Heiser signode['first'] = (not self.names) 188556aa6d5SMarkus Heiser self.state.document.note_explicit_target(signode) 189556aa6d5SMarkus Heiser inv = self.env.domaindata['c']['objects'] 190556aa6d5SMarkus Heiser if (name in inv and self.env.config.nitpicky): 191556aa6d5SMarkus Heiser if self.objtype == 'function': 192556aa6d5SMarkus Heiser if ('c:func', name) not in self.env.config.nitpick_ignore: 193556aa6d5SMarkus Heiser self.state_machine.reporter.warning( 194556aa6d5SMarkus Heiser 'duplicate C object description of %s, ' % name + 195556aa6d5SMarkus Heiser 'other instance in ' + self.env.doc2path(inv[name][0]), 196556aa6d5SMarkus Heiser line=self.lineno) 197556aa6d5SMarkus Heiser inv[name] = (self.env.docname, self.objtype) 198556aa6d5SMarkus Heiser 199556aa6d5SMarkus Heiser indextext = self.get_index_text(name) 200556aa6d5SMarkus Heiser if indextext: 201b495360eSMarkus Heiser if major == 1 and minor < 4: 202b495360eSMarkus Heiser # indexnode's tuple changed in 1.4 203b495360eSMarkus Heiser # https://github.com/sphinx-doc/sphinx/commit/e6a5a3a92e938fcd75866b4227db9e0524d58f7c 204b495360eSMarkus Heiser self.indexnode['entries'].append( 205b495360eSMarkus Heiser ('single', indextext, targetname, '')) 206b495360eSMarkus Heiser else: 207b495360eSMarkus Heiser self.indexnode['entries'].append( 208b495360eSMarkus Heiser ('single', indextext, targetname, '', None)) 209e8f5c617SMarkus Heiser 210e8f5c617SMarkus Heiserclass CDomain(Base_CDomain): 211e8f5c617SMarkus Heiser 212e8f5c617SMarkus Heiser """C language domain.""" 213e8f5c617SMarkus Heiser name = 'c' 214e8f5c617SMarkus Heiser label = 'C' 215e8f5c617SMarkus Heiser directives = { 216e8f5c617SMarkus Heiser 'function': CObject, 217e8f5c617SMarkus Heiser 'member': CObject, 218e8f5c617SMarkus Heiser 'macro': CObject, 219e8f5c617SMarkus Heiser 'type': CObject, 220e8f5c617SMarkus Heiser 'var': CObject, 221e8f5c617SMarkus Heiser } 222