]> git.donarmstrong.com Git - neurodebian.git/blob - sphinx/ext/quote.py
dirty but working version of quoting
[neurodebian.git] / sphinx / ext / quote.py
1 #emacs: -*- coding: utf-8; mode: python-mode; py-indent-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2 #ex: set sts=4 ts=4 sw=4 et:
3 """
4    ext.quote
5    ~~~~~~~~~
6
7    Compile the quotes
8
9    :copyright: Copyright 2010 Yaroslav O. Halchenko
10    :license: BSD
11 """
12
13 import operator
14 from random import sample
15
16 from docutils import nodes
17 from docutils.parsers.rst import directives, Directive
18 from docutils.parsers.rst.directives import body
19
20 from sphinx.environment import NoUri
21 from sphinx.util.compat import Directive, make_admonition
22
23
24 class quote_node(nodes.Body, nodes.Element): pass
25 class quotes(nodes.General, nodes.Element): pass
26
27 def _info(msg):
28         # print "I: ", msg
29         pass
30
31 def _prep_tags(options):
32         """Extract tags listed in a string"""
33         options['tags'] = set((x.strip()
34                                                    for x in
35                                                    options.get('tags', '').split(',')))
36
37 class Quote(Directive):
38     """
39     A quote entry, displayed (if configured) in the form of an admonition.
40     """
41
42     has_content = True
43     required_arguments = 0
44     optional_arguments = 0
45     final_argument_whitespace = False
46         option_spec = {
47                 'author': directives.unchanged,
48                 'affiliation': directives.unchanged,
49                 'date': directives.unchanged,
50                 'group': directives.unchanged,
51                 'tags': directives.unchanged,   # list of tags per quote
52                 'source': directives.unchanged}
53
54     def run(self):
55         state = self.state
56         env = self.state.document.settings.env
57         options = self.options
58                 if hasattr(env, 'new_serialno'):
59                         targetid = 'index-%s' % env.new_serialno('index')
60                 else:
61                         targetid = "index-%s" % env.index_num
62         targetnode = nodes.target('', '', ids=[targetid])
63                 targetnode['classes'] = ['epigraph']
64
65         node = quote_node()
66                 node += nodes.block_quote(
67                         '',
68                         nodes.paragraph('', '\n'.join(self.content), classes=['text']))
69                 #state.nested_parse(self.content, self.content_offset, node)
70
71         for element in node:
72             if isinstance(element, nodes.block_quote):
73                 element['classes'] += ['epigraph']
74
75                 signode = [nodes.attribution('--', '--')]
76                 # Embed all components within attributions
77                 siglb = nodes.line_block('')
78                 # Pre-format some
79                 if 'date' in options:
80                         options['date'] = '[%(date)s]' % options
81                 if 'source' in options:
82                         options['source'] = 'Source: %(source)s' % options
83                 for el in ['author', 'date', 'affiliation', 'source']:
84                         if el in options:
85                                 siglb += [nodes.inline('', '  '+options[el], classes=[el])]
86                 signode[0].extend(siglb)
87                 node[0].extend(signode)
88                 node.line = self.lineno
89                 # tune up options
90                 _prep_tags(self.options)
91                 node.options = options
92         return [targetnode] + [node]
93
94
95
96 def process_quotes(app, doctree):
97     # collect all quotes in the environment
98     # this is not done in the directive itself because it some transformations
99     # must have already been run, e.g. substitutions
100         _info("process_quotes")
101
102     env = app.builder.env
103     if not hasattr(env, 'quote_all_quotes'):
104         env.quote_all_quotes = []
105     for node in doctree.traverse(quote_node):
106         try:
107             targetnode = node.parent[node.parent.index(node) - 1]
108             if not isinstance(targetnode, nodes.target):
109                 raise IndexError
110         except IndexError:
111             targetnode = None
112         env.quote_all_quotes.append({
113             'docname': env.docname,
114             'lineno': node.line,
115             'quote': node, #.deepcopy(),
116             'target': targetnode,
117         })
118
119
120 class Quotes(Directive):
121     """
122     A list of all quote entries.
123     """
124
125     has_content = False
126     required_arguments = 0
127     optional_arguments = 0
128     final_argument_whitespace = False
129         option_spec = {
130                 'random': directives.positive_int, # how many to randomly output
131                 'group': directives.unchanged,     # what group to show
132                 'tags': directives.unchanged,   # list of tags to be matched
133                 #'sections': lambda a: directives.choice(a, ('date', 'group'))
134                 }
135
136     def run(self):
137         # Simply insert an empty quotes node which will be replaced later
138         # when process_quote_nodes is called
139                 res = quotes('')
140                 # tags which must be matched
141                 if 'tags' in self.options:
142                         _prep_tags(self.options)
143                 res.options = self.options
144                 _info("Run Quotes %s" % res)
145         return [res]
146
147
148
149 def process_quote_nodes(app, doctree, fromdocname):
150     ## if not app.config['quote_include_quotes']:
151     ##     for node in doctree.traverse(quote_node):
152     ##         node.parent.remove(node)
153
154     # Replace all quotes nodes with a list of the collected quotes.
155     # Augment each quote with a backlink to the original location.
156     env = app.builder.env
157
158     if not hasattr(env, 'quote_all_quotes'):
159         env.quote_all_quotes = []
160
161         #_info("process_quote_nodes '%s' %i"
162         #         % (fromdocname, len(env.quote_all_quotes)))
163
164     for node in doctree.traverse(quotes):
165         ## if not app.config['quote_include_quotes']:
166         ##     node.replace_self([])
167         ##     continue
168
169         content = []
170                 filters = []
171                 loptions = node.options
172
173                 # Filter by tags?
174                 if 'tags' in loptions:
175                         ltags = loptions['tags']
176                         filters.append(
177                                 lambda quote: set.issuperset(
178                                         quote.options['tags'], ltags))
179                 # Filter by group?
180                 if 'group' in loptions:
181                         loptions['group']
182                         filters.append(
183                                 lambda quote:
184                                 quote.options.get('group', '') == loptions['group'])
185
186         for quote_info in env.quote_all_quotes:
187             quote_entry = quote_info['quote']
188                         if not reduce(operator.__and__,
189                                                   [f(quote_entry) for f in filters],
190                                                   True):
191                                 continue
192                         ## Commented out mechanism to back-reference original
193                         ## location
194                         ##
195             ## para = nodes.paragraph(classes=['quote-source'])
196             ## filename = env.doc2path(quote_info['docname'], base=None)
197             ## description = _('(The <<original entry>> is located in '
198             ##                 ' %s, line %d.)') % (filename, quote_info['lineno'])
199             ## desc1 = description[:description.find('<<')]
200             ## desc2 = description[description.find('>>')+2:]
201             ## para += nodes.Text(desc1, desc1)
202
203             ## # Create a reference
204             ## newnode = nodes.reference('', '', internal=True)
205             ## innernode = nodes.emphasis(_('original entry'), _('original entry'))
206             ## try:
207             ##     newnode['refuri'] = app.builder.get_relative_uri(
208             ##         fromdocname, quote_info['docname'])
209             ##     newnode['refuri'] += '#' + quote_info['target']['refid']
210             ## except NoUri:
211             ##     # ignore if no URI can be determined, e.g. for LaTeX output
212             ##     pass
213             ## newnode.append(innernode)
214             ## para += newnode
215             ## para += nodes.Text(desc2, desc2)
216             ## #para += nodes.Text("XXX", "YYY")
217
218             # (Recursively) resolve references in the quote content
219             env.resolve_references(quote_entry, quote_info['docname'],
220                                    app.builder)
221
222             # Insert into the quotes
223             content.append(quote_entry)
224             ## content.append(para)
225
226                 # Handle additional quotes options
227
228                 # Select a limited number of random samples
229                 if loptions.get('random', None):
230                         # Randomly select desired number of quotes
231                         content = sample(content, loptions['random'])
232
233                 # Group items into sections and intersperse the list
234                 # with section nodes
235                 if loptions.get('sections', None):
236                         raise NotImplementedError
237                         term = loptions['sections']
238                         terms = [q.options.get(term, None) for q in content]
239                         terms = list(set([x for x in terms if x]))
240                         # Simply sort according to what to group on,
241                         # and then insert sections?
242                         #import pydb
243                         #pydb.debugger()
244                         section = nodes.section('')
245                         section.extend(nodes.title('', 'XXX'))
246                         section += content[:2]
247                         section.parent = content[0].parent
248                         content = [ section ]
249
250                 # Substitute with the content
251         node.replace_self(content)
252
253
254
255 def purge_quotes(app, env, docname):
256     if not hasattr(env, 'quote_all_quotes'):
257         return
258         _info("purge_quotes")
259     env.quote_all_quotes = [quote for quote in env.quote_all_quotes
260                           if quote['docname'] != docname]
261
262
263 def quotes_noop(self, node):
264         pass
265
266 def setup(app):
267         ## app.add_config_value('quotes_include_quotes', False, False)
268         #import pydb
269         #pydb.debugger()
270     app.add_node(quotes)
271     app.add_node(quote_node,
272                  html=(quotes_noop, quotes_noop),
273                  latex=(quotes_noop, quotes_noop),
274                  text=(quotes_noop, quotes_noop))
275
276     app.add_directive('quote', Quote)
277     app.add_directive('quotes', Quotes)
278     app.connect('doctree-read', process_quotes)
279     app.connect('doctree-resolved', process_quote_nodes)
280     app.connect('env-purge-doc', purge_quotes)