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