sphinxcontrib-cmakedomain / sphinxcontrib / cmakedomain.py

# -*- coding: utf-8 -*-
"""
    sphinx_domain_cmake
    ~~~~~~~~~~~~~~~~~~~

    A CMake domain.

    :copyright: 2012 by Kay-Uwe (Kiwi) Lorenz, ModuleWorks GmbH
    :license: BSD, see LICENSE for details.
"""

from sphinxcontrib.domaintools import custom_domain
import re

from docutils import nodes
from sphinx import addnodes
from sphinx.domains import Domain, ObjType
from sphinx.domains.std import GenericObject
from sphinx.locale import l_, _
from sphinx.directives import ObjectDescription
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode
from sphinx.util.compat import Directive

cmake_param_desc_re = re.compile(
    r'([-_a-zA-Z0-9]+)\s+(.*)')


macro_sig_re = re.compile(r'(\w+)\(([^)]*)\)')
_term = r'''
    <\w+>(?:\.\.\.)?
    | 
       \[
       (?: [^\[\]]+
         | (?=\[) \[ [^\[\]]+ \]  # allow one nesting level
       )+
       \]
    |
       \{
       (?:[^{}]+
         | (?=\{) \{ [^{}]+ \}    # allow one nesting level
       )+
       \}(?:\.\.\.)?
'''  
macro_param_re = re.compile(r'''
    %s | (?P<key>[^\s]+)\s+(?P<value>(?:%s)) | (?P<flag>[^\s]+)
    ''' % (_term,_term), re.VERBOSE)

class desc_cmake_argumentlist(nodes.Part, nodes.Inline, nodes.TextElement):
    """Node for a general parameter list."""
    child_text_separator = ' '

def argumentlist_visit(self, node):
    self.visit_desc_parameterlist(node)

def argumentlist_depart(self, node):
    self.depart_desc_parameterlist(node)

def html_argumentlist_visit(self, node):
    self.visit_desc_parameterlist(node)

    if len(node.children) > 3:
        self.body.append('<span class="long-argument-list">')
    else:
        self.body.append('<span class="argument-list">')


def html_argumentlist_depart(self, node):
    self.body.append('</span>')

    self.depart_desc_parameterlist(node)

class desc_cmake_argument(nodes.Part, nodes.Inline, nodes.TextElement):
    """Node for an argument wrapper"""

def argument_visit(self, node):
    pass

def argument_depart(self, node):
    pass

def html_argument_visit(self, node):
    self.body.append('<span class="arg">')

def html_argument_depart(self, node):
    self.body.append("</span>")

#class desc_cmake_argumentlist(addnodes.desc_parameterlist):
    #child_text_separator = " "

# sphinx.HTMLTranslator should be derived from GenericNodeVisitor, but it
# is not
#import sphinx.writers.html as _html
#kjksetattr(_html.


def _get_param_node(m):
    if m.group('key'):
        node = addnodes.desc_parameter()

        key = nodes.strong(m.group('key'), m.group('key'))
        key['classes'].append('arg-key')
        node += key

        node += nodes.Text(" ", " ")
        value = nodes.inline(m.group('value'), m.group('value'))
        value['classes'].append('arg-value')
        node += value

        return node

    elif m.group('flag'):
        node = addnodes.desc_parameter()
        flag = nodes.strong(m.group('flag'), m.group('flag'))
        flag['classes'].append('arg-flag')
        node += flag
        return flag
        
    else:
        return addnodes.desc_parameter(m.group(0), m.group(0))
                

def parse_macro(env, sig, signode):
    #import rpdb2 ; rpdb2.start_embedded_debugger('foo')
    m = macro_sig_re.match(sig)
    if not m:
        signode += addnodes.desc_name(sig, sig)
        return sig
    name, args = m.groups()
    signode += addnodes.desc_name(name, name)
    plist = desc_cmake_argumentlist()
    for m in macro_param_re.finditer(args):
        arg = m.group(0)

        if arg.startswith('['):
            arg = arg[1:-1].strip()
            x = desc_cmake_argument()
            opt = addnodes.desc_optional()
            x += opt
            m = macro_param_re.match(arg)

            assert m is not None, "%s does not match %s" % (arg, macro_param_re.pattern)

            opt += _get_param_node(m)
            plist += x

        # elif arg.startswith('{') choice
        else:
            x = desc_cmake_argument()
            x += _get_param_node(m)
            plist += x

    signode += plist
    return name
    
def setup(app):
    from sphinx.util.docfields import GroupedField
    app.add_node(
        node = desc_cmake_argumentlist,
        html = (html_argumentlist_visit, html_argumentlist_depart),
        latex = (argumentlist_visit, argumentlist_depart),
    )

    app.add_node(
        node = desc_cmake_argument,
        html = (html_argument_visit, html_argument_depart),
        latex = (argument_visit, argument_depart),
        )

    app.add_domain(custom_domain('CMakeDomain',
        name  = 'cmake',
        label = "CMake", 

        elements = dict(
            macro = dict(
                # role = 'xxx' if differs from macro
                # objtype = 'xxx' if differs from macro
                objname       = "CMake Macro",
                indextemplate = "pair: %s; CMake macro",
                parse         = parse_macro,
                fields        = [ 
                    GroupedField('parameter',
                        label = "Parameters",
                        names = [ 'param' ])
                ]
            ),

            var   = dict(
                objname = "CMake Variable",
                indextemplate = "pair: %s; CMake variable"
            ),
        )))

# vim: ts=4 : sw=4 : et