1
# Copyright (C) 2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""A sphinx/docutil writer producing texinfo output."""
19
from docutils import (
25
class TexinfoWriter(writers.Writer):
27
supported = ('texinfo',)
28
settings_spec = ('No options here.', '', ())
29
settings_defaults = {}
33
def __init__(self, builder):
34
writers.Writer.__init__(self)
35
self.builder = builder
38
visitor = TexinfoTranslator(self.document, self.builder)
39
self.document.walkabout(visitor)
40
self.output = visitor.body
43
class TexinfoTranslator(nodes.NodeVisitor):
45
# Sphinx and texinfo doesn't use the same names for the section levels,
46
# since this can be confusing, here are the correspondances (sphinx ->
50
# section -> subsection
51
# subsection -> subsubsection
52
# Additionally, sphinx defines subsubsections and paragraphs
53
section_names = ['chapter', 'section', 'subsection', 'subsubsection']
54
"""texinfo section names differ from the sphinx ones.
56
Since this can be confusing, the correspondences are shown below
61
subsection -> subsubsection
63
Additionally, sphinx defines subsubsections and paragraphs.
66
def __init__(self, document, builder):
67
nodes.NodeVisitor.__init__(self, document)
69
# toctree uses some nodes for different purposes (namely:
70
# caompact_paragraph, bullet_list, reference, list_item) that needs to
71
# know when they are proessing a toctree. The following attributes take
73
self.in_toctree = False
74
self.toctree_current_ref = None
75
# sections can be embedded and produce different directives depending
77
self.section_level = -1
78
# The title text is in a Text node that shouldn't be output literally
80
# Tables has some specific nodes but need more help
82
self.tab_nb_cols = None
83
self.tab_item_cmd = None
84
self.tab_tab_cmd = None
85
self.tab_entry_num = None
87
def add_text(self, text):
88
self.chunks.append(text)
92
def visit_document(self, node):
93
# The debug killer trick
94
# print node.pformat()
97
def depart_document(self, node):
98
self.body = ''.join(chunk for chunk in self.chunks)
102
def visit_section(self, node):
103
self.section_level += 1
105
def depart_section(self, node):
106
self.section_level -= 1
108
def visit_topic(self, node):
111
def depart_topic(self, node):
114
def visit_paragraph(self, node):
117
def depart_paragraph(self, node):
118
if not self.in_table:
119
# End the paragraph with a new line and leave a blank line after
121
self.add_text('\n\n')
123
def visit_compact_paragraph(self, node):
124
if node.has_key('toctree'):
125
self.in_toctree = True
126
self.add_text('@menu\n')
127
elif self.in_toctree:
128
self.toctree_current_ref = None
130
def depart_compact_paragraph(self, node):
131
if node.has_key('toctree'):
132
self.add_text('@end menu\n')
133
self.in_toctree = False
134
elif self.in_toctree:
135
# * FIRST-ENTRY-NAME:(FILENAME)NODENAME. DESCRIPTION
136
entry_name = node.astext()
137
# XXX: the file name should probably be adjusted to the targeted
139
file_name = self.toctree_current_ref
140
node_name = entry_name
142
# XXX: What if :maxdepth: is not 1 ?
143
self.add_text('* %s:(%s)%s. %s\n' % (entry_name, file_name,
144
node_name, description))
145
self.toctree_current_ref = None
147
# End the paragraph with a new line and leave a blank line after it.
148
self.add_text('\n\n')
150
def visit_literal_block(self, node):
151
self.add_text('@samp{')
153
def depart_literal_block(self, node):
156
def visit_block_quote(self, node):
159
def depart_block_quote(self, node):
162
def visit_note(self, node):
165
def depart_warning(self, node):
168
def visit_warning(self, node):
171
def depart_note(self, node):
174
def visit_footnote(self, node):
177
def depart_footnote(self, node):
180
def visit_comment(self, node):
183
# Document attributes
185
def visit_title(self, node):
188
section_name = self.section_names[self.section_level]
190
# Just use @heading, it's not numbered anyway
191
section_name = 'heading'
192
self.add_text('@%s %s\n' % (section_name, node.astext()))
194
def depart_title(self, node):
195
self.in_title = False
197
def visit_label(self, node):
200
def visit_substitution_definition(self, node):
205
def visit_Text(self, node):
208
def depart_Text(self, node):
209
if not self.in_toctree and not self.in_title and not self.in_table:
212
text = text.replace('@', '@@')
214
text = text.replace('{', '@{')
216
text = text.replace('}', '@}')
221
def visit_emphasis(self, node):
222
self.add_text('@emph{')
224
def depart_emphasis(self, node):
227
def visit_strong(self, node):
228
self.add_text('@strong{')
230
def depart_strong(self, node):
233
def visit_literal(self, node):
234
self.add_text('@code{')
236
def depart_literal(self, node):
241
def visit_bullet_list(self, node):
245
self.add_text('@itemize @bullet\n')
247
def depart_bullet_list(self, node):
251
self.add_text('@end itemize\n')
253
def visit_enumerated_list(self, node):
254
self.add_text('@enumerate\n')
256
def depart_enumerated_list(self, node):
257
self.add_text('@end enumerate\n')
259
def visit_definition_list(self, node):
262
def depart_definition_list(self, node):
265
def visit_definition_list_item(self, node):
268
def depart_definition_list_item(self, node):
271
def visit_term(self, node):
274
def depart_term(self, node):
277
def visit_definition(self, node):
280
def depart_definition(self, node):
283
def visit_field_list(self, node):
285
def depart_field_list(self, node):
288
def visit_field(self, node):
290
def depart_field(self, node):
293
def visit_field_name(self, node):
296
def depart_field_name(self, node):
299
def visit_field_body(self, node):
302
def depart_field_body(self, node):
305
def visit_list_item(self, node):
306
if not self.in_toctree:
307
self.add_text('@item\n')
309
def depart_list_item(self, node):
310
# The item contains a paragraph which already ends with a blank line.
313
def visit_option_list(self, node):
316
def depart_option_list(self, node):
319
def visit_option_list_item(self, node):
322
def depart_option_list_item(self, node):
325
def visit_option_group(self, node):
328
def depart_option_group(self, node):
331
def visit_option(self, node):
334
def depart_option(self, node):
337
def visit_option_string(self, node):
339
def depart_option_string(self, node):
342
def visit_option_argument(self, node):
345
def depart_option_argument(self, node):
348
def visit_description(self, node):
350
def depart_description(self, node):
354
def visit_table(self, node):
356
self.add_text('@multitable ')
358
def depart_table(self, node):
359
self.add_text('@end multitable\n')
360
# Leave a blank line after a table
362
self.in_table = False
364
def visit_tgroup(self, node):
365
self.tab_nb_cols = node['cols']
367
def depart_tgroup(self, node):
368
self.tab_nb_cols = None
370
def visit_colspec(self, node):
371
self.add_text('{%s}' % ('x' * node['colwidth']))
373
def depart_colspec(self, node):
374
self.tab_nb_cols -= 1
375
if self.tab_nb_cols == 0:
376
self.add_text('\n') # end the @multitable line
378
def visit_thead(self, node):
379
self.tab_item_cmd = '@headitem %s '
380
self.tab_tab_cmd = '@tab %s'
382
def depart_thead(self, node):
384
self.tab_item_cmd = None
385
self.tab_tab_cmd = None
387
def visit_tbody(self, node):
388
self.tab_item_cmd = '@item %s\n'
389
self.tab_tab_cmd = '@tab %s\n'
391
def depart_tbody(self, node):
392
self.tab_item_cmd = None
393
self.tab_tab_cmd = None
395
def visit_row(self, node):
396
self.tab_entry_num = 0
398
def depart_row(self, node):
399
self.tab_entry_num = None
401
def visit_entry(self, node):
402
if self.tab_entry_num == 0:
403
cmd = self.tab_item_cmd
405
cmd = self.tab_tab_cmd
406
self.add_text(cmd % node.astext())
407
self.tab_entry_num += 1
409
def depart_entry(self, node):
414
def visit_reference(self, node):
415
uri = node.get('refuri', '')
417
self.toctree_current_ref = uri
419
def depart_reference(self, node):
422
def visit_footnote_reference(self, node):
425
def visit_citation_reference(self, node):
428
def visit_title_reference(self, node):
431
def depart_title_reference(self, node):
434
def visit_target(self, node):
437
def depart_target(self, node):
440
def visit_image(self, node):
441
self.add_text(_('[image]'))