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 | |
---|
14 | import re #@UnusedImport |
---|
15 | import string #@UnusedImport |
---|
16 | |
---|
17 | from docutils import nodes #@UnusedImport |
---|
18 | from docutils.parsers.rst import directives #@UnusedImport |
---|
19 | |
---|
20 | from sphinx import addnodes #@UnusedImport |
---|
21 | from sphinx.roles import XRefRole #@UnusedImport |
---|
22 | from sphinx.locale import l_, _ #@UnusedImport |
---|
23 | from sphinx.directives import ObjectDescription #@UnusedImport |
---|
24 | from sphinx.domains import Domain, ObjType, Index #@UnusedImport |
---|
25 | from sphinx.util.compat import Directive #@UnusedImport |
---|
26 | from sphinx.util.nodes import make_refnode #@UnusedImport |
---|
27 | from sphinx.util.docfields import Field, TypedField #@UnusedImport |
---|
28 | |
---|
29 | |
---|
30 | match_all = '.*' |
---|
31 | non_greedy_filler = match_all+'?' |
---|
32 | double_quote_string_match = '("'+non_greedy_filler+'")' |
---|
33 | word_match = '((?:[a-z_]\w*))' |
---|
34 | cdef_match = '(cdef)' |
---|
35 | |
---|
36 | |
---|
37 | spec_macro_sig_re = re.compile( |
---|
38 | r'''^ ([a-zA-Z_]\w*) # macro name |
---|
39 | ''', re.VERBOSE) |
---|
40 | |
---|
41 | spec_func_sig_re = re.compile(word_match+'\(' |
---|
42 | + '('+match_all+')' |
---|
43 | + '\)', |
---|
44 | re.IGNORECASE|re.DOTALL) |
---|
45 | |
---|
46 | spec_cdef_name_sig_re = re.compile(double_quote_string_match, re.IGNORECASE|re.DOTALL) |
---|
47 | |
---|
48 | |
---|
49 | class 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 | |
---|
115 | class SpecVariableObject(ObjectDescription): |
---|
116 | """ |
---|
117 | Description of a SPEC variable |
---|
118 | """ |
---|
119 | |
---|
120 | |
---|
121 | class 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 | |
---|
155 | class 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 | |
---|
208 | def setup(app): |
---|
209 | app.add_domain(SpecDomain) |
---|
210 | # http://sphinx.pocoo.org/ext/appapi.html#sphinx.domains.Domain |
---|