Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
textile.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """
3  source.textile
4  ~~~~~~~~~~~~~~
5 
6  Textile Sphinx builder.
7 
8  Derived from Sphinx's plain-text sphinx builder.
9 
10  NOTE: NOT COMPLETED YET.
11 
12  :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
13  Copyright 2011 by Kouhei Sutou.
14  :license: BSD, see LICENSE for details.
15 """
16 
17 # For builder.
18 import codecs
19 from os import path
20 
21 from docutils.io import StringOutput
22 
23 from sphinx.builders import Builder
24 from sphinx.util.osutil import ensuredir, os_path
25 
26 class TextileBuilder(Builder):
27  name = 'textile'
28  format = 'textile'
29  out_suffix = '.textile'
30 
31  def init(self):
32  pass
33 
34  def get_outdated_docs(self):
35  for docname in self.env.found_docs:
36  if docname not in self.env.all_docs:
37  yield docname
38  continue
39  targetname = self.env.doc2path(docname, self.outdir,
40  self.out_suffix)
41  try:
42  targetmtime = path.getmtime(targetname)
43  except Exception:
44  targetmtime = 0
45  try:
46  srcmtime = path.getmtime(self.env.doc2path(docname))
47  if srcmtime > targetmtime:
48  yield docname
49  except EnvironmentError:
50  # source doesn't exist anymore
51  pass
52 
53  def get_target_uri(self, docname, typ=None):
54  return ''
55 
56  def prepare_writing(self, docnames):
57  self.writer = TextileWriter(self)
58 
59  def write_doc(self, docname, doctree):
60  destination = StringOutput(encoding='utf-8')
61  self.writer.write(doctree, destination)
62  outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
63  ensuredir(path.dirname(outfilename))
64  try:
65  f = codecs.open(outfilename, 'w', 'utf-8')
66  try:
67  f.write(self.writer.output)
68  finally:
69  f.close()
70  except (IOError, OSError), err:
71  self.warn("error writing file %s: %s" % (outfilename, err))
72 
73  def finish(self):
74  pass
75 
76 # For writer.
77 import re
78 import textwrap
79 
80 from docutils import nodes, writers
81 
82 from sphinx import addnodes
83 from sphinx.locale import admonitionlabels, versionlabels, _
84 
85 class TextileWriter(writers.Writer):
86  supported = ('textile',)
87  settings_spec = ('No options here.', '', ())
88  settings_defaults = {}
89 
90  output = None
91 
92  def __init__(self, builder):
93  writers.Writer.__init__(self)
94  self.builder = builder
95 
96  def translate(self):
97  visitor = TextileTranslator(self.document, self.builder)
98  self.document.walkabout(visitor)
99  self.output = visitor.body
100 
101 
102 MAXWIDTH = 700
103 STDINDENT = 3
104 
105 class TextileTranslator(nodes.NodeVisitor):
106  sectionchars = '*=-~"+'
107 
108  def __init__(self, document, builder):
109  nodes.NodeVisitor.__init__(self, document)
110 
111  self.states = [[]]
112  self.stateindent = [0]
113  self.list_counter = []
114  self.sectionlevel = 0
115  self.table = None
116 
117  def add_text(self, text):
118  self.states[-1].append((-1, text))
119  def new_state(self, indent=STDINDENT):
120  self.states.append([])
121  self.stateindent.append(indent)
122  def end_state(self, wrap=True, end=[''], first=None):
123  content = self.states.pop()
124  maxindent = sum(self.stateindent)
125  indent = self.stateindent.pop()
126  result = []
127  toformat = []
128  def do_format():
129  if not toformat:
130  return
131  if wrap:
132  res = textwrap.wrap(''.join(toformat), width=MAXWIDTH-maxindent)
133  else:
134  res = ''.join(toformat).splitlines()
135  if end:
136  res += end
137  result.append((indent, res))
138  for itemindent, item in content:
139  if itemindent == -1:
140  toformat.append(item)
141  else:
142  do_format()
143  result.append((indent + itemindent, item))
144  toformat = []
145  do_format()
146  if first is not None and result:
147  itemindent, item = result[0]
148  if item:
149  result.insert(0, (itemindent - indent, [first + item[0]]))
150  result[1] = (itemindent, item[1:])
151  self.states[-1].extend(result)
152 
153  def visit_document(self, node):
154  self.new_state(0)
155  def depart_document(self, node):
156  self.end_state()
157  self.body = '\n'.join(line and (' '*indent + line)
158  for indent, lines in self.states[0]
159  for line in lines)
160  # XXX header/footer?
161 
162  def visit_highlightlang(self, node):
163  raise nodes.SkipNode
164 
165  def visit_section(self, node):
166  self._title_char = self.sectionchars[self.sectionlevel]
167  self.sectionlevel += 1
168  def depart_section(self, node):
169  self.sectionlevel -= 1
170 
171  def visit_topic(self, node):
172  self.new_state(0)
173  def depart_topic(self, node):
174  self.end_state()
175 
176  visit_sidebar = visit_topic
177  depart_sidebar = depart_topic
178 
179  def visit_rubric(self, node):
180  self.new_state(0)
181  self.add_text('-[ ')
182  def depart_rubric(self, node):
183  self.add_text(' ]-')
184  self.end_state()
185 
186  def visit_compound(self, node):
187  pass
188  def depart_compound(self, node):
189  pass
190 
191  def visit_glossary(self, node):
192  pass
193  def depart_glossary(self, node):
194  pass
195 
196  def visit_title(self, node):
197  if isinstance(node.parent, nodes.Admonition):
198  self.add_text(node.astext()+': ')
199  raise nodes.SkipNode
200  self.new_state(0)
201  def depart_title(self, node):
202  text = ''.join(x[1] for x in self.states.pop() if x[0] == -1)
203  text = "h%d. %s" % (self.sectionlevel, text)
204  self.stateindent.pop()
205  self.states[-1].append((0, ['', text, '']))
206 
207  def visit_subtitle(self, node):
208  pass
209  def depart_subtitle(self, node):
210  pass
211 
212  def visit_attribution(self, node):
213  self.add_text('-- ')
214  def depart_attribution(self, node):
215  pass
216 
217  def visit_desc(self, node):
218  pass
219  def depart_desc(self, node):
220  pass
221 
222  def visit_desc_signature(self, node):
223  self.new_state(0)
224  if node.parent['objtype'] in ('class', 'exception'):
225  self.add_text('%s ' % node.parent['objtype'])
226  def depart_desc_signature(self, node):
227  # XXX: wrap signatures in a way that makes sense
228  self.end_state(wrap=False, end=None)
229 
230  def visit_desc_name(self, node):
231  pass
232  def depart_desc_name(self, node):
233  pass
234 
235  def visit_desc_addname(self, node):
236  pass
237  def depart_desc_addname(self, node):
238  pass
239 
240  def visit_desc_type(self, node):
241  pass
242  def depart_desc_type(self, node):
243  pass
244 
245  def visit_desc_returns(self, node):
246  self.add_text(' -> ')
247  def depart_desc_returns(self, node):
248  pass
249 
250  def visit_desc_parameterlist(self, node):
251  self.add_text('(')
252  self.first_param = 1
253  def depart_desc_parameterlist(self, node):
254  self.add_text(')')
255 
256  def visit_desc_parameter(self, node):
257  if not self.first_param:
258  self.add_text(', ')
259  else:
260  self.first_param = 0
261  self.add_text(node.astext())
262  raise nodes.SkipNode
263 
264  def visit_desc_optional(self, node):
265  self.add_text('[')
266  def depart_desc_optional(self, node):
267  self.add_text(']')
268 
269  def visit_desc_annotation(self, node):
270  pass
271  def depart_desc_annotation(self, node):
272  pass
273 
274  def visit_refcount(self, node):
275  pass
276  def depart_refcount(self, node):
277  pass
278 
279  def visit_desc_content(self, node):
280  self.new_state()
281  self.add_text('\n')
282  def depart_desc_content(self, node):
283  self.end_state()
284 
285  def visit_figure(self, node):
286  self.new_state()
287  def depart_figure(self, node):
288  self.end_state()
289 
290  def visit_caption(self, node):
291  pass
292  def depart_caption(self, node):
293  pass
294 
295  def visit_productionlist(self, node):
296  self.new_state()
297  names = []
298  for production in node:
299  names.append(production['tokenname'])
300  maxlen = max(len(name) for name in names)
301  for production in node:
302  if production['tokenname']:
303  self.add_text(production['tokenname'].ljust(maxlen) + ' ::=')
304  lastname = production['tokenname']
305  else:
306  self.add_text('%s ' % (' '*len(lastname)))
307  self.add_text(production.astext() + '\n')
308  self.end_state(wrap=False)
309  raise nodes.SkipNode
310 
311  def visit_seealso(self, node):
312  self.new_state()
313  def depart_seealso(self, node):
314  self.end_state(first='')
315 
316  def visit_footnote(self, node):
317  self._footnote = node.children[0].astext().strip()
318  self.new_state(len(self._footnote) + 3)
319  def depart_footnote(self, node):
320  self.end_state(first='[%s] ' % self._footnote)
321 
322  def visit_citation(self, node):
323  if len(node) and isinstance(node[0], nodes.label):
324  self._citlabel = node[0].astext()
325  else:
326  self._citlabel = ''
327  self.new_state(len(self._citlabel) + 3)
328  def depart_citation(self, node):
329  self.end_state(first='[%s] ' % self._citlabel)
330 
331  def visit_label(self, node):
332  raise nodes.SkipNode
333 
334  # XXX: option list could use some better styling
335 
336  def visit_option_list(self, node):
337  pass
338  def depart_option_list(self, node):
339  pass
340 
341  def visit_option_list_item(self, node):
342  self.new_state(0)
343  def depart_option_list_item(self, node):
344  self.end_state()
345 
346  def visit_option_group(self, node):
347  self._firstoption = True
348  def depart_option_group(self, node):
349  self.add_text(' ')
350 
351  def visit_option(self, node):
352  if self._firstoption:
353  self._firstoption = False
354  else:
355  self.add_text(', ')
356  def depart_option(self, node):
357  pass
358 
359  def visit_option_string(self, node):
360  pass
361  def depart_option_string(self, node):
362  pass
363 
364  def visit_option_argument(self, node):
365  self.add_text(node['delimiter'])
366  def depart_option_argument(self, node):
367  pass
368 
369  def visit_description(self, node):
370  pass
371  def depart_description(self, node):
372  pass
373 
374  def visit_tabular_col_spec(self, node):
375  raise nodes.SkipNode
376 
377  def visit_colspec(self, node):
378  self.table[0].append(node['colwidth'])
379  raise nodes.SkipNode
380 
381  def visit_tgroup(self, node):
382  pass
383  def depart_tgroup(self, node):
384  pass
385 
386  def visit_thead(self, node):
387  pass
388  def depart_thead(self, node):
389  pass
390 
391  def visit_tbody(self, node):
392  self.table.append('sep')
393  def depart_tbody(self, node):
394  pass
395 
396  def visit_row(self, node):
397  self.table.append([])
398  def depart_row(self, node):
399  pass
400 
401  def visit_entry(self, node):
402  if node.has_key('morerows') or node.has_key('morecols'):
403  raise NotImplementedError('Column or row spanning cells are '
404  'not implemented.')
405  self.new_state(0)
406  def depart_entry(self, node):
407  text = '\n'.join('\n'.join(x[1]) for x in self.states.pop())
408  self.stateindent.pop()
409  self.table[-1].append(text)
410 
411  def visit_table(self, node):
412  if self.table:
413  raise NotImplementedError('Nested tables are not supported.')
414  self.new_state(0)
415  self.table = [[]]
416  def depart_table(self, node):
417  lines = self.table[1:]
418  fmted_rows = []
419  colwidths = self.table[0]
420  realwidths = colwidths[:]
421  separator = 0
422  # don't allow paragraphs in table cells for now
423  for line in lines:
424  if line == 'sep':
425  separator = len(fmted_rows)
426  else:
427  cells = []
428  for i, cell in enumerate(line):
429  par = textwrap.wrap(cell, width=colwidths[i])
430  if par:
431  maxwidth = max(map(len, par))
432  else:
433  maxwidth = 0
434  realwidths[i] = max(realwidths[i], maxwidth)
435  cells.append(par)
436  fmted_rows.append(cells)
437 
438  def writesep(char='-'):
439  out = ['+']
440  for width in realwidths:
441  out.append(char * (width+2))
442  out.append('+')
443  self.add_text(''.join(out) + '\n')
444 
445  def writerow(row):
446  lines = map(None, *row)
447  for line in lines:
448  out = ['|']
449  for i, cell in enumerate(line):
450  if cell:
451  out.append(' ' + cell.ljust(realwidths[i]+1))
452  else:
453  out.append(' ' * (realwidths[i] + 2))
454  out.append('|')
455  self.add_text(''.join(out) + '\n')
456 
457  for i, row in enumerate(fmted_rows):
458  if separator and i == separator:
459  writesep('=')
460  else:
461  writesep('-')
462  writerow(row)
463  writesep('-')
464  self.table = None
465  self.end_state(wrap=False)
466 
467  def visit_acks(self, node):
468  self.new_state(0)
469  self.add_text(', '.join(n.astext() for n in node.children[0].children)
470  + '.')
471  self.end_state()
472  raise nodes.SkipNode
473 
474  def visit_image(self, node):
475  self.add_text(_('[image]'))
476  raise nodes.SkipNode
477 
478  def visit_transition(self, node):
479  indent = sum(self.stateindent)
480  self.new_state(0)
481  self.add_text('=' * (MAXWIDTH - indent))
482  self.end_state()
483  raise nodes.SkipNode
484 
485  def visit_bullet_list(self, node):
486  self.list_counter.append(-1)
487  def depart_bullet_list(self, node):
488  self.list_counter.pop()
489 
490  def visit_enumerated_list(self, node):
491  self.list_counter.append(0)
492  def depart_enumerated_list(self, node):
493  self.list_counter.pop()
494 
495  def visit_definition_list(self, node):
496  self.list_counter.append(-2)
497  def depart_definition_list(self, node):
498  self.list_counter.pop()
499 
500  def visit_list_item(self, node):
501  if self.list_counter[-1] == -1:
502  # bullet list
503  self.new_state(2)
504  elif self.list_counter[-1] == -2:
505  # definition list
506  pass
507  else:
508  # enumerated list
509  self.list_counter[-1] += 1
510  self.new_state(len(str(self.list_counter[-1])) + 2)
511  def depart_list_item(self, node):
512  if self.list_counter[-1] == -1:
513  self.end_state(first='* ', end=None)
514  elif self.list_counter[-1] == -2:
515  pass
516  else:
517  self.end_state(first='%s. ' % self.list_counter[-1], end=None)
518 
519  def visit_definition_list_item(self, node):
520  self._li_has_classifier = len(node) >= 2 and \
521  isinstance(node[1], nodes.classifier)
523  pass
524 
525  def visit_term(self, node):
526  self.new_state(0)
527  def depart_term(self, node):
528  if not self._li_has_classifier:
529  self.end_state(end=None)
530 
531  def visit_classifier(self, node):
532  self.add_text(' : ')
533  def depart_classifier(self, node):
534  self.end_state(end=None)
535 
536  def visit_definition(self, node):
537  self.new_state()
538  def depart_definition(self, node):
539  self.end_state()
540 
541  def visit_field_list(self, node):
542  pass
543  def depart_field_list(self, node):
544  pass
545 
546  def visit_field(self, node):
547  pass
548  def depart_field(self, node):
549  pass
550 
551  def visit_field_name(self, node):
552  self.new_state(0)
553  def depart_field_name(self, node):
554  self.add_text(':')
555  self.end_state(end=None)
556 
557  def visit_field_body(self, node):
558  self.new_state()
559  def depart_field_body(self, node):
560  self.end_state()
561 
562  def visit_centered(self, node):
563  pass
564  def depart_centered(self, node):
565  pass
566 
567  def visit_hlist(self, node):
568  pass
569  def depart_hlist(self, node):
570  pass
571 
572  def visit_hlistcol(self, node):
573  pass
574  def depart_hlistcol(self, node):
575  pass
576 
577  def visit_admonition(self, node):
578  self.new_state(0)
579  def depart_admonition(self, node):
580  self.end_state()
581 
582  def _visit_admonition(self, node):
583  self.new_state(2)
584  def _make_depart_admonition(name):
585  def depart_admonition(self, node):
586  self.end_state(first=admonitionlabels[name] + ': ')
587  return depart_admonition
588 
589  visit_attention = _visit_admonition
590  depart_attention = _make_depart_admonition('attention')
591  visit_caution = _visit_admonition
592  depart_caution = _make_depart_admonition('caution')
593  visit_danger = _visit_admonition
594  depart_danger = _make_depart_admonition('danger')
595  visit_error = _visit_admonition
596  depart_error = _make_depart_admonition('error')
597  visit_hint = _visit_admonition
598  depart_hint = _make_depart_admonition('hint')
599  visit_important = _visit_admonition
600  depart_important = _make_depart_admonition('important')
601  visit_note = _visit_admonition
602  depart_note = _make_depart_admonition('note')
603  visit_tip = _visit_admonition
604  depart_tip = _make_depart_admonition('tip')
605  visit_warning = _visit_admonition
606  depart_warning = _make_depart_admonition('warning')
607 
608  def visit_versionmodified(self, node):
609  self.new_state(0)
610  if node.children:
611  self.add_text(versionlabels[node['type']] % node['version'] + ': ')
612  else:
613  self.add_text(versionlabels[node['type']] % node['version'] + '.')
614  def depart_versionmodified(self, node):
615  self.end_state()
616 
617  def visit_literal_block(self, node):
618  self.new_state()
619  def depart_literal_block(self, node):
620  self.end_state(wrap=False)
621 
622  def visit_doctest_block(self, node):
623  self.new_state(0)
624  def depart_doctest_block(self, node):
625  self.end_state(wrap=False)
626 
627  def visit_line_block(self, node):
628  self.new_state(0)
629  def depart_line_block(self, node):
630  self.end_state(wrap=False)
631 
632  def visit_line(self, node):
633  pass
634  def depart_line(self, node):
635  pass
636 
637  def visit_block_quote(self, node):
638  self.new_state()
639  def depart_block_quote(self, node):
640  self.end_state()
641 
642  def visit_compact_paragraph(self, node):
643  pass
644  def depart_compact_paragraph(self, node):
645  pass
646 
647  def visit_paragraph(self, node):
648  if not isinstance(node.parent, nodes.Admonition) or \
649  isinstance(node.parent, addnodes.seealso):
650  self.new_state(0)
651  def depart_paragraph(self, node):
652  if not isinstance(node.parent, nodes.Admonition) or \
653  isinstance(node.parent, addnodes.seealso):
654  self.end_state()
655 
656  def visit_target(self, node):
657  raise nodes.SkipNode
658 
659  def visit_index(self, node):
660  raise nodes.SkipNode
661 
663  raise nodes.SkipNode
664 
665  def visit_pending_xref(self, node):
666  pass
667  def depart_pending_xref(self, node):
668  pass
669 
670  def visit_reference(self, node):
671  pass
672  def depart_reference(self, node):
673  pass
674 
675  def visit_download_reference(self, node):
676  pass
677  def depart_download_reference(self, node):
678  pass
679 
680  def visit_emphasis(self, node):
681  self.add_text('*')
682  def depart_emphasis(self, node):
683  self.add_text('*')
684 
685  def visit_literal_emphasis(self, node):
686  self.add_text('*')
687  def depart_literal_emphasis(self, node):
688  self.add_text('*')
689 
690  def visit_strong(self, node):
691  self.add_text('**')
692  def depart_strong(self, node):
693  self.add_text('**')
694 
695  def visit_abbreviation(self, node):
696  self.add_text('')
697  def depart_abbreviation(self, node):
698  if node.hasattr('explanation'):
699  self.add_text(' (%s)' % node['explanation'])
700 
701  def visit_title_reference(self, node):
702  self.add_text('*')
703  def depart_title_reference(self, node):
704  self.add_text('*')
705 
706  def visit_literal(self, node):
707  self.add_text('``')
708  def depart_literal(self, node):
709  self.add_text('``')
710 
711  def visit_subscript(self, node):
712  self.add_text('_')
713  def depart_subscript(self, node):
714  pass
715 
716  def visit_superscript(self, node):
717  self.add_text('^')
718  def depart_superscript(self, node):
719  pass
720 
721  def visit_footnote_reference(self, node):
722  self.add_text('[%s]' % node.astext())
723  raise nodes.SkipNode
724 
725  def visit_citation_reference(self, node):
726  self.add_text('[%s]' % node.astext())
727  raise nodes.SkipNode
728 
729  def visit_Text(self, node):
730  self.add_text(node.astext())
731  def depart_Text(self, node):
732  pass
733 
734  def visit_generated(self, node):
735  pass
736  def depart_generated(self, node):
737  pass
738 
739  def visit_inline(self, node):
740  pass
741  def depart_inline(self, node):
742  pass
743 
744  def visit_problematic(self, node):
745  self.add_text('>>')
746  def depart_problematic(self, node):
747  self.add_text('<<')
748 
749  def visit_system_message(self, node):
750  self.new_state(0)
751  self.add_text('<SYSTEM MESSAGE: %s>' % node.astext())
752  self.end_state()
753  raise nodes.SkipNode
754 
755  def visit_comment(self, node):
756  raise nodes.SkipNode
757 
758  def visit_meta(self, node):
759  # only valid for HTML
760  raise nodes.SkipNode
761 
762  def visit_raw(self, node):
763  if 'text' in node.get('format', '').split():
764  self.body.append(node.astext())
765  raise nodes.SkipNode
766 
767  def unknown_visit(self, node):
768  raise NotImplementedError('Unknown node: ' + node.__class__.__name__)
769 
770 def setup(app):
771  app.add_builder(TextileBuilder)