source: specdomain/src/specdomain/sphinxcontrib/specdomain.py @ 937

Last change on this file since 937 was 937, checked in by jemian, 11 years ago

refs #8, document the commands, as they exist now

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 7.7 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3    sphinxcontrib.specdomain
4    ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6    SPEC domain.
7
8    :copyright: Copyright 2012 by Pete Jemian
9    :license: BSD, see LICENSE for details.
10"""
11
12# http://sphinx.pocoo.org/ext/appapi.html
13
14import re                                               #@UnusedImport
15import string                                           #@UnusedImport
16
17from docutils import nodes                              #@UnusedImport
18from docutils.parsers.rst import directives             #@UnusedImport
19
20from sphinx import addnodes                             #@UnusedImport
21from sphinx.roles import XRefRole                       #@UnusedImport
22from sphinx.locale import l_, _                         #@UnusedImport
23from sphinx.directives import ObjectDescription         #@UnusedImport
24from sphinx.domains import Domain, ObjType, Index       #@UnusedImport
25from sphinx.util.compat import Directive                #@UnusedImport
26from sphinx.util.nodes import make_refnode              #@UnusedImport
27from sphinx.util.docfields import Field, TypedField     #@UnusedImport
28
29
30match_all                   = '.*'
31non_greedy_filler           = match_all+'?'
32double_quote_string_match   = '("'+non_greedy_filler+'")'
33word_match                  = '((?:[a-z_]\w*))'
34cdef_match                  = '(cdef)'
35
36
37spec_macro_sig_re = re.compile(
38                               r'''^ ([a-zA-Z_]\w*)         # macro name
39                               ''', re.VERBOSE)
40
41spec_func_sig_re = re.compile(word_match+'\('
42                      + '('+match_all+')' 
43                      + '\)', 
44                      re.IGNORECASE|re.DOTALL)
45
46spec_cdef_name_sig_re = re.compile(double_quote_string_match, re.IGNORECASE|re.DOTALL)
47
48
49class SpecMacroObject(ObjectDescription):
50    """
51    Description of a SPEC macro definition
52    """
53
54    doc_field_types = [
55        TypedField('parameter', label=l_('Parameters'),
56                   names=('param', 'parameter', 'arg', 'argument',
57                          'keyword', 'kwarg', 'kwparam'),
58                   typerolename='def', typenames=('paramtype', 'type'),
59                   can_collapse=True),
60        Field('returnvalue', label=l_('Returns'), has_arg=False,
61              names=('returns', 'return')),
62        Field('returntype', label=l_('Return type'), has_arg=False,
63              names=('rtype',)),
64    ]
65
66    def add_target_and_index(self, name, sig, signode):
67        targetname = '%s-%s' % (self.objtype, name)
68        signode['ids'].append(targetname)
69        self.state.document.note_explicit_target(signode)
70        indextext = self._get_index_text(name)
71        if indextext:
72            self.indexnode['entries'].append(('single', indextext,
73                                              targetname, ''))
74
75    def _get_index_text(self, name):
76        macro_types = {
77            'def':  'SPEC macro definition; %s',
78            'rdef': 'SPEC run-time macro definition; %s',
79            'cdef': 'SPEC chained macro definition; %s',
80        }
81        if self.objtype in macro_types:
82            return _(macro_types[self.objtype]) % name
83        else:
84            return ''
85
86    def handle_signature(self, sig, signode):
87        # Must be able to match these (without preceding def or rdef)
88        #     def macro_name
89        #     def macro_name()
90        #     def macro_name(arg1, arg2)
91        #     rdef macro_name
92        #     cdef("macro_name", "content", "groupname", flags)
93        m = spec_func_sig_re.match(sig) or spec_macro_sig_re.match(sig)
94        if m is None:
95            raise ValueError
96        arglist = m.groups()
97        name = arglist[0]
98        args = []
99        if len(arglist) > 1:
100            args = arglist[1:]
101            if name == 'cdef':
102                # TODO: need to match complete arg list
103                # several different signatures are possible (see cdef-examples.mac)
104                # for now, just get the macro name and ignore the arg list
105                m = spec_cdef_name_sig_re.match(args[0])
106                arglist = m.groups()
107                name = arglist[0].strip('"')
108                args = ['<<< cdef argument list not handled yet >>>']       # FIXME:
109        signode += addnodes.desc_name(name, name)
110        if len(args) > 0:
111            signode += addnodes.desc_addname(args, args)
112        return name
113
114
115class SpecVariableObject(ObjectDescription):
116    """
117    Description of a SPEC variable
118    """
119
120
121class SpecXRefRole(XRefRole):
122    """ """
123   
124    def process_link(self, env, refnode, has_explicit_title, title, target):
125        key = ":".join((refnode['refdomain'], refnode['reftype']))
126        refnode[key] = env.temp_data.get(key)        # key was 'spec:def'
127        if not has_explicit_title:
128            title = title.lstrip(':')   # only has a meaning for the target
129            target = target.lstrip('~') # only has a meaning for the title
130            # if the first character is a tilde, don't display the module/class
131            # parts of the contents
132            if title[0:1] == '~':
133                title = title[1:]
134                colon = title.rfind(':')
135                if colon != -1:
136                    title = title[colon+1:]
137        return title, target
138
139    def result_nodes(self, document, env, node, is_ref):
140        # this code adds index entries for each role instance
141        if not is_ref:
142            return [node], []
143        varname = node['reftarget']
144        tgtid = 'index-%s' % env.new_serialno('index')
145        indexnode = addnodes.index()
146        indexnode['entries'] = [
147            ('single', varname, tgtid, ''),
148            #('single', _('environment variable; %s') % varname, tgtid, ''),
149        ]
150        targetnode = nodes.target('', '', ids=[tgtid])
151        document.note_explicit_target(targetnode)
152        return [indexnode, targetnode, node], []
153
154
155class SpecDomain(Domain):
156    """SPEC language domain."""
157   
158    name = 'spec'
159    label = 'SPEC, http://www.certif.com'
160    object_types = {    # type of object that a domain can document
161        'def':    ObjType(l_('def'),    'def'),
162        'rdef':   ObjType(l_('rdef'),   'rdef'),
163        'cdef':   ObjType(l_('cdef'),   'cdef'),
164        'global': ObjType(l_('global'), 'global'),
165        'local':  ObjType(l_('local'),  'local'),
166    }
167    directives = {
168        'def':          SpecMacroObject,
169        'rdef':         SpecMacroObject,
170        'cdef':         SpecMacroObject,
171        'global':       SpecVariableObject,
172        'local':        SpecVariableObject,
173    }
174    roles = {
175        'def' :     SpecXRefRole(),
176        'rdef':     SpecXRefRole(),
177        'cdef':     SpecXRefRole(),
178        'global':   SpecXRefRole(),
179        'local':    SpecXRefRole(),
180    }
181    initial_data = {
182        'objects': {}, # fullname -> docname, objtype
183    }
184
185    def clear_doc(self, docname):
186        for (typ, name), doc in self.data['objects'].items():
187            if doc == docname:
188                del self.data['objects'][typ, name]
189
190    def resolve_xref(self, env, fromdocname, builder, typ, target, node,
191                     contnode):
192        objects = self.data['objects']
193        objtypes = self.objtypes_for_role(typ)
194        for objtype in objtypes:
195            if (objtype, target) in objects:
196                return make_refnode(builder, fromdocname,
197                                    objects[objtype, target],
198                                    objtype + '-' + target,
199                                    contnode, target + ' ' + objtype)
200
201    def get_objects(self):
202        for (typ, name), docname in self.data['objects'].iteritems():
203            yield name, name, typ, docname, typ + '-' + name, 1
204
205
206# http://sphinx.pocoo.org/ext/tutorial.html#the-setup-function
207
208def setup(app):
209    app.add_domain(SpecDomain)
210    # http://sphinx.pocoo.org/ext/appapi.html#sphinx.domains.Domain
Note: See TracBrowser for help on using the repository browser.