##// END OF EJS Templates
spelling: Structured
timeless@mozdev.org -
r17517:15036492 default
parent child Browse files
Show More
@@ -1,1110 +1,1110 b''
1 1 # -*- coding: utf-8 -*-
2 2 # $Id: manpage.py 6110 2009-08-31 14:40:33Z grubert $
3 3 # Author: Engelbert Gruber <grubert@users.sourceforge.net>
4 4 # Copyright: This module is put into the public domain.
5 5
6 6 """
7 7 Simple man page writer for reStructuredText.
8 8
9 9 Man pages (short for "manual pages") contain system documentation on unix-like
10 10 systems. The pages are grouped in numbered sections:
11 11
12 12 1 executable programs and shell commands
13 13 2 system calls
14 14 3 library functions
15 15 4 special files
16 16 5 file formats
17 17 6 games
18 18 7 miscellaneous
19 19 8 system administration
20 20
21 21 Man pages are written *troff*, a text file formatting system.
22 22
23 23 See http://www.tldp.org/HOWTO/Man-Page for a start.
24 24
25 25 Man pages have no subsection only parts.
26 26 Standard parts
27 27
28 28 NAME ,
29 29 SYNOPSIS ,
30 30 DESCRIPTION ,
31 31 OPTIONS ,
32 32 FILES ,
33 33 SEE ALSO ,
34 34 BUGS ,
35 35
36 36 and
37 37
38 38 AUTHOR .
39 39
40 40 A unix-like system keeps an index of the DESCRIPTIONs, which is accesable
41 41 by the command whatis or apropos.
42 42
43 43 """
44 44
45 45 __docformat__ = 'reStructuredText'
46 46
47 47 import re
48 48
49 49 from docutils import nodes, writers, languages
50 50 try:
51 51 import roman
52 52 except ImportError:
53 53 from docutils.utils import roman
54 54 import inspect
55 55
56 56 FIELD_LIST_INDENT = 7
57 57 DEFINITION_LIST_INDENT = 7
58 58 OPTION_LIST_INDENT = 7
59 59 BLOCKQOUTE_INDENT = 3.5
60 60
61 61 # Define two macros so man/roff can calculate the
62 62 # indent/unindent margins by itself
63 63 MACRO_DEF = (r""".
64 64 .nr rst2man-indent-level 0
65 65 .
66 66 .de1 rstReportMargin
67 67 \\$1 \\n[an-margin]
68 68 level \\n[rst2man-indent-level]
69 69 level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
70 70 -
71 71 \\n[rst2man-indent0]
72 72 \\n[rst2man-indent1]
73 73 \\n[rst2man-indent2]
74 74 ..
75 75 .de1 INDENT
76 76 .\" .rstReportMargin pre:
77 77 . RS \\$1
78 78 . nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin]
79 79 . nr rst2man-indent-level +1
80 80 .\" .rstReportMargin post:
81 81 ..
82 82 .de UNINDENT
83 83 . RE
84 84 .\" indent \\n[an-margin]
85 85 .\" old: \\n[rst2man-indent\\n[rst2man-indent-level]]
86 86 .nr rst2man-indent-level -1
87 87 .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
88 88 .in \\n[rst2man-indent\\n[rst2man-indent-level]]u
89 89 ..
90 90 """)
91 91
92 92 class Writer(writers.Writer):
93 93
94 94 supported = ('manpage')
95 95 """Formats this writer supports."""
96 96
97 97 output = None
98 98 """Final translated form of `document`."""
99 99
100 100 def __init__(self):
101 101 writers.Writer.__init__(self)
102 102 self.translator_class = Translator
103 103
104 104 def translate(self):
105 105 visitor = self.translator_class(self.document)
106 106 self.document.walkabout(visitor)
107 107 self.output = visitor.astext()
108 108
109 109
110 110 class Table(object):
111 111 def __init__(self):
112 112 self._rows = []
113 113 self._options = ['center']
114 114 self._tab_char = '\t'
115 115 self._coldefs = []
116 116 def new_row(self):
117 117 self._rows.append([])
118 118 def append_separator(self, separator):
119 119 """Append the separator for table head."""
120 120 self._rows.append([separator])
121 121 def append_cell(self, cell_lines):
122 122 """cell_lines is an array of lines"""
123 123 start = 0
124 124 if len(cell_lines) > 0 and cell_lines[0] == '.sp\n':
125 125 start = 1
126 126 self._rows[-1].append(cell_lines[start:])
127 127 if len(self._coldefs) < len(self._rows[-1]):
128 128 self._coldefs.append('l')
129 129 def _minimize_cell(self, cell_lines):
130 130 """Remove leading and trailing blank and ``.sp`` lines"""
131 131 while (cell_lines and cell_lines[0] in ('\n', '.sp\n')):
132 132 del cell_lines[0]
133 133 while (cell_lines and cell_lines[-1] in ('\n', '.sp\n')):
134 134 del cell_lines[-1]
135 135 def as_list(self):
136 136 text = ['.TS\n']
137 137 text.append(' '.join(self._options) + ';\n')
138 138 text.append('|%s|.\n' % ('|'.join(self._coldefs)))
139 139 for row in self._rows:
140 140 # row = array of cells. cell = array of lines.
141 141 text.append('_\n') # line above
142 142 text.append('T{\n')
143 143 for i in range(len(row)):
144 144 cell = row[i]
145 145 self._minimize_cell(cell)
146 146 text.extend(cell)
147 147 if not text[-1].endswith('\n'):
148 148 text[-1] += '\n'
149 149 if i < len(row)-1:
150 150 text.append('T}'+self._tab_char+'T{\n')
151 151 else:
152 152 text.append('T}\n')
153 153 text.append('_\n')
154 154 text.append('.TE\n')
155 155 return text
156 156
157 157 class Translator(nodes.NodeVisitor):
158 158 """"""
159 159
160 160 words_and_spaces = re.compile(r'\S+| +|\n')
161 document_start = """Man page generated from reStructeredText."""
161 document_start = """Man page generated from reStructuredText."""
162 162
163 163 def __init__(self, document):
164 164 nodes.NodeVisitor.__init__(self, document)
165 165 self.settings = settings = document.settings
166 166 lcode = settings.language_code
167 167 arglen = len(inspect.getargspec(languages.get_language)[0])
168 168 if arglen == 2:
169 169 self.language = languages.get_language(lcode,
170 170 self.document.reporter)
171 171 else:
172 172 self.language = languages.get_language(lcode)
173 173 self.head = []
174 174 self.body = []
175 175 self.foot = []
176 176 self.section_level = 0
177 177 self.context = []
178 178 self.topic_class = ''
179 179 self.colspecs = []
180 180 self.compact_p = 1
181 181 self.compact_simple = None
182 182 # the list style "*" bullet or "#" numbered
183 183 self._list_char = []
184 184 # writing the header .TH and .SH NAME is postboned after
185 185 # docinfo.
186 186 self._docinfo = {
187 187 "title" : "", "title_upper": "",
188 188 "subtitle" : "",
189 189 "manual_section" : "", "manual_group" : "",
190 190 "author" : [],
191 191 "date" : "",
192 192 "copyright" : "",
193 193 "version" : "",
194 194 }
195 195 self._docinfo_keys = [] # a list to keep the sequence as in source.
196 196 self._docinfo_names = {} # to get name from text not normalized.
197 197 self._in_docinfo = None
198 198 self._active_table = None
199 199 self._in_literal = False
200 200 self.header_written = 0
201 201 self._line_block = 0
202 202 self.authors = []
203 203 self.section_level = 0
204 204 self._indent = [0]
205 205 # central definition of simple processing rules
206 206 # what to output on : visit, depart
207 207 # Do not use paragraph requests ``.PP`` because these set indentation.
208 208 # use ``.sp``. Remove superfluous ``.sp`` in ``astext``.
209 209 #
210 210 # Fonts are put on a stack, the top one is used.
211 211 # ``.ft P`` or ``\\fP`` pop from stack.
212 212 # ``B`` bold, ``I`` italic, ``R`` roman should be available.
213 213 # Hopefully ``C`` courier too.
214 214 self.defs = {
215 215 'indent' : ('.INDENT %.1f\n', '.UNINDENT\n'),
216 216 'definition_list_item' : ('.TP', ''),
217 217 'field_name' : ('.TP\n.B ', '\n'),
218 218 'literal' : ('\\fB', '\\fP'),
219 219 'literal_block' : ('.sp\n.nf\n.ft C\n', '\n.ft P\n.fi\n'),
220 220
221 221 'option_list_item' : ('.TP\n', ''),
222 222
223 223 'reference' : (r'\%', r'\:'),
224 224 'emphasis': ('\\fI', '\\fP'),
225 225 'strong' : ('\\fB', '\\fP'),
226 226 'term' : ('\n.B ', '\n'),
227 227 'title_reference' : ('\\fI', '\\fP'),
228 228
229 229 'topic-title' : ('.SS ',),
230 230 'sidebar-title' : ('.SS ',),
231 231
232 232 'problematic' : ('\n.nf\n', '\n.fi\n'),
233 233 }
234 234 # NOTE don't specify the newline before a dot-command, but ensure
235 235 # it is there.
236 236
237 237 def comment_begin(self, text):
238 238 """Return commented version of the passed text WITHOUT end of
239 239 line/comment."""
240 240 prefix = '.\\" '
241 241 out_text = ''.join(
242 242 [(prefix + in_line + '\n')
243 243 for in_line in text.split('\n')])
244 244 return out_text
245 245
246 246 def comment(self, text):
247 247 """Return commented version of the passed text."""
248 248 return self.comment_begin(text)+'.\n'
249 249
250 250 def ensure_eol(self):
251 251 """Ensure the last line in body is terminated by new line."""
252 252 if self.body[-1][-1] != '\n':
253 253 self.body.append('\n')
254 254
255 255 def astext(self):
256 256 """Return the final formatted document as a string."""
257 257 if not self.header_written:
258 258 # ensure we get a ".TH" as viewers require it.
259 259 self.head.append(self.header())
260 260 # filter body
261 261 for i in xrange(len(self.body)-1, 0, -1):
262 262 # remove superfluous vertical gaps.
263 263 if self.body[i] == '.sp\n':
264 264 if self.body[i - 1][:4] in ('.BI ','.IP '):
265 265 self.body[i] = '.\n'
266 266 elif (self.body[i - 1][:3] == '.B ' and
267 267 self.body[i - 2][:4] == '.TP\n'):
268 268 self.body[i] = '.\n'
269 269 elif (self.body[i - 1] == '\n' and
270 270 self.body[i - 2][0] != '.' and
271 271 (self.body[i - 3][:7] == '.TP\n.B '
272 272 or self.body[i - 3][:4] == '\n.B ')
273 273 ):
274 274 self.body[i] = '.\n'
275 275 return ''.join(self.head + self.body + self.foot)
276 276
277 277 def deunicode(self, text):
278 278 text = text.replace(u'\xa0', '\\ ')
279 279 text = text.replace(u'\u2020', '\\(dg')
280 280 return text
281 281
282 282 def visit_Text(self, node):
283 283 text = node.astext()
284 284 text = text.replace('\\','\\e')
285 285 replace_pairs = [
286 286 (u'-', ur'\-'),
287 287 (u'\'', ur'\(aq'),
288 288 (u'Β΄', ur'\''),
289 289 (u'`', ur'\(ga'),
290 290 ]
291 291 for (in_char, out_markup) in replace_pairs:
292 292 text = text.replace(in_char, out_markup)
293 293 # unicode
294 294 text = self.deunicode(text)
295 295 if self._in_literal:
296 296 # prevent interpretation of "." at line start
297 297 if text[0] == '.':
298 298 text = '\\&' + text
299 299 text = text.replace('\n.', '\n\\&.')
300 300 self.body.append(text)
301 301
302 302 def depart_Text(self, node):
303 303 pass
304 304
305 305 def list_start(self, node):
306 306 class enum_char(object):
307 307 enum_style = {
308 308 'bullet' : '\\(bu',
309 309 'emdash' : '\\(em',
310 310 }
311 311
312 312 def __init__(self, style):
313 313 self._style = style
314 314 if 'start' in node:
315 315 self._cnt = node['start'] - 1
316 316 else:
317 317 self._cnt = 0
318 318 self._indent = 2
319 319 if style == 'arabic':
320 320 # indentation depends on number of childrens
321 321 # and start value.
322 322 self._indent = len(str(len(node.children)))
323 323 self._indent += len(str(self._cnt)) + 1
324 324 elif style == 'loweralpha':
325 325 self._cnt += ord('a') - 1
326 326 self._indent = 3
327 327 elif style == 'upperalpha':
328 328 self._cnt += ord('A') - 1
329 329 self._indent = 3
330 330 elif style.endswith('roman'):
331 331 self._indent = 5
332 332
333 333 def next(self):
334 334 if self._style == 'bullet':
335 335 return self.enum_style[self._style]
336 336 elif self._style == 'emdash':
337 337 return self.enum_style[self._style]
338 338 self._cnt += 1
339 339 # TODO add prefix postfix
340 340 if self._style == 'arabic':
341 341 return "%d." % self._cnt
342 342 elif self._style in ('loweralpha', 'upperalpha'):
343 343 return "%c." % self._cnt
344 344 elif self._style.endswith('roman'):
345 345 res = roman.toRoman(self._cnt) + '.'
346 346 if self._style.startswith('upper'):
347 347 return res.upper()
348 348 return res.lower()
349 349 else:
350 350 return "%d." % self._cnt
351 351 def get_width(self):
352 352 return self._indent
353 353 def __repr__(self):
354 354 return 'enum_style-%s' % list(self._style)
355 355
356 356 if 'enumtype' in node:
357 357 self._list_char.append(enum_char(node['enumtype']))
358 358 else:
359 359 self._list_char.append(enum_char('bullet'))
360 360 if len(self._list_char) > 1:
361 361 # indent nested lists
362 362 self.indent(self._list_char[-2].get_width())
363 363 else:
364 364 self.indent(self._list_char[-1].get_width())
365 365
366 366 def list_end(self):
367 367 self.dedent()
368 368 self._list_char.pop()
369 369
370 370 def header(self):
371 371 tmpl = (".TH %(title_upper)s %(manual_section)s"
372 372 " \"%(date)s\" \"%(version)s\" \"%(manual_group)s\"\n"
373 373 ".SH NAME\n"
374 374 "%(title)s \- %(subtitle)s\n")
375 375 return tmpl % self._docinfo
376 376
377 377 def append_header(self):
378 378 """append header with .TH and .SH NAME"""
379 379 # NOTE before everything
380 380 # .TH title_upper section date source manual
381 381 if self.header_written:
382 382 return
383 383 self.body.append(self.header())
384 384 self.body.append(MACRO_DEF)
385 385 self.header_written = 1
386 386
387 387 def visit_address(self, node):
388 388 self.visit_docinfo_item(node, 'address')
389 389
390 390 def depart_address(self, node):
391 391 pass
392 392
393 393 def visit_admonition(self, node, name=None):
394 394 if name:
395 395 self.body.append('.IP %s\n' %
396 396 self.language.labels.get(name, name))
397 397
398 398 def depart_admonition(self, node):
399 399 self.body.append('.RE\n')
400 400
401 401 def visit_attention(self, node):
402 402 self.visit_admonition(node, 'attention')
403 403
404 404 depart_attention = depart_admonition
405 405
406 406 def visit_docinfo_item(self, node, name):
407 407 if name == 'author':
408 408 self._docinfo[name].append(node.astext())
409 409 else:
410 410 self._docinfo[name] = node.astext()
411 411 self._docinfo_keys.append(name)
412 412 raise nodes.SkipNode
413 413
414 414 def depart_docinfo_item(self, node):
415 415 pass
416 416
417 417 def visit_author(self, node):
418 418 self.visit_docinfo_item(node, 'author')
419 419
420 420 depart_author = depart_docinfo_item
421 421
422 422 def visit_authors(self, node):
423 423 # _author is called anyway.
424 424 pass
425 425
426 426 def depart_authors(self, node):
427 427 pass
428 428
429 429 def visit_block_quote(self, node):
430 430 # BUG/HACK: indent alway uses the _last_ indention,
431 431 # thus we need two of them.
432 432 self.indent(BLOCKQOUTE_INDENT)
433 433 self.indent(0)
434 434
435 435 def depart_block_quote(self, node):
436 436 self.dedent()
437 437 self.dedent()
438 438
439 439 def visit_bullet_list(self, node):
440 440 self.list_start(node)
441 441
442 442 def depart_bullet_list(self, node):
443 443 self.list_end()
444 444
445 445 def visit_caption(self, node):
446 446 pass
447 447
448 448 def depart_caption(self, node):
449 449 pass
450 450
451 451 def visit_caution(self, node):
452 452 self.visit_admonition(node, 'caution')
453 453
454 454 depart_caution = depart_admonition
455 455
456 456 def visit_citation(self, node):
457 457 num, text = node.astext().split(None, 1)
458 458 num = num.strip()
459 459 self.body.append('.IP [%s] 5\n' % num)
460 460
461 461 def depart_citation(self, node):
462 462 pass
463 463
464 464 def visit_citation_reference(self, node):
465 465 self.body.append('['+node.astext()+']')
466 466 raise nodes.SkipNode
467 467
468 468 def visit_classifier(self, node):
469 469 pass
470 470
471 471 def depart_classifier(self, node):
472 472 pass
473 473
474 474 def visit_colspec(self, node):
475 475 self.colspecs.append(node)
476 476
477 477 def depart_colspec(self, node):
478 478 pass
479 479
480 480 def write_colspecs(self):
481 481 self.body.append("%s.\n" % ('L '*len(self.colspecs)))
482 482
483 483 def visit_comment(self, node,
484 484 sub=re.compile('-(?=-)').sub):
485 485 self.body.append(self.comment(node.astext()))
486 486 raise nodes.SkipNode
487 487
488 488 def visit_contact(self, node):
489 489 self.visit_docinfo_item(node, 'contact')
490 490
491 491 depart_contact = depart_docinfo_item
492 492
493 493 def visit_container(self, node):
494 494 pass
495 495
496 496 def depart_container(self, node):
497 497 pass
498 498
499 499 def visit_compound(self, node):
500 500 pass
501 501
502 502 def depart_compound(self, node):
503 503 pass
504 504
505 505 def visit_copyright(self, node):
506 506 self.visit_docinfo_item(node, 'copyright')
507 507
508 508 def visit_danger(self, node):
509 509 self.visit_admonition(node, 'danger')
510 510
511 511 depart_danger = depart_admonition
512 512
513 513 def visit_date(self, node):
514 514 self.visit_docinfo_item(node, 'date')
515 515
516 516 def visit_decoration(self, node):
517 517 pass
518 518
519 519 def depart_decoration(self, node):
520 520 pass
521 521
522 522 def visit_definition(self, node):
523 523 pass
524 524
525 525 def depart_definition(self, node):
526 526 pass
527 527
528 528 def visit_definition_list(self, node):
529 529 self.indent(DEFINITION_LIST_INDENT)
530 530
531 531 def depart_definition_list(self, node):
532 532 self.dedent()
533 533
534 534 def visit_definition_list_item(self, node):
535 535 self.body.append(self.defs['definition_list_item'][0])
536 536
537 537 def depart_definition_list_item(self, node):
538 538 self.body.append(self.defs['definition_list_item'][1])
539 539
540 540 def visit_description(self, node):
541 541 pass
542 542
543 543 def depart_description(self, node):
544 544 pass
545 545
546 546 def visit_docinfo(self, node):
547 547 self._in_docinfo = 1
548 548
549 549 def depart_docinfo(self, node):
550 550 self._in_docinfo = None
551 551 # NOTE nothing should be written before this
552 552 self.append_header()
553 553
554 554 def visit_doctest_block(self, node):
555 555 self.body.append(self.defs['literal_block'][0])
556 556 self._in_literal = True
557 557
558 558 def depart_doctest_block(self, node):
559 559 self._in_literal = False
560 560 self.body.append(self.defs['literal_block'][1])
561 561
562 562 def visit_document(self, node):
563 563 # no blank line between comment and header.
564 564 self.body.append(self.comment(self.document_start).rstrip()+'\n')
565 565 # writing header is postboned
566 566 self.header_written = 0
567 567
568 568 def depart_document(self, node):
569 569 if self._docinfo['author']:
570 570 self.body.append('.SH AUTHOR\n%s\n'
571 571 % ', '.join(self._docinfo['author']))
572 572 skip = ('author', 'copyright', 'date',
573 573 'manual_group', 'manual_section',
574 574 'subtitle',
575 575 'title', 'title_upper', 'version')
576 576 for name in self._docinfo_keys:
577 577 if name == 'address':
578 578 self.body.append("\n%s:\n%s%s.nf\n%s\n.fi\n%s%s" % (
579 579 self.language.labels.get(name, name),
580 580 self.defs['indent'][0] % 0,
581 581 self.defs['indent'][0] % BLOCKQOUTE_INDENT,
582 582 self._docinfo[name],
583 583 self.defs['indent'][1],
584 584 self.defs['indent'][1]))
585 585 elif name not in skip:
586 586 if name in self._docinfo_names:
587 587 label = self._docinfo_names[name]
588 588 else:
589 589 label = self.language.labels.get(name, name)
590 590 self.body.append("\n%s: %s\n" % (label, self._docinfo[name]))
591 591 if self._docinfo['copyright']:
592 592 self.body.append('.SH COPYRIGHT\n%s\n'
593 593 % self._docinfo['copyright'])
594 594 self.body.append(self.comment(
595 595 'Generated by docutils manpage writer.\n'))
596 596
597 597 def visit_emphasis(self, node):
598 598 self.body.append(self.defs['emphasis'][0])
599 599
600 600 def depart_emphasis(self, node):
601 601 self.body.append(self.defs['emphasis'][1])
602 602
603 603 def visit_entry(self, node):
604 604 # a cell in a table row
605 605 if 'morerows' in node:
606 606 self.document.reporter.warning('"table row spanning" not supported',
607 607 base_node=node)
608 608 if 'morecols' in node:
609 609 self.document.reporter.warning(
610 610 '"table cell spanning" not supported', base_node=node)
611 611 self.context.append(len(self.body))
612 612
613 613 def depart_entry(self, node):
614 614 start = self.context.pop()
615 615 self._active_table.append_cell(self.body[start:])
616 616 del self.body[start:]
617 617
618 618 def visit_enumerated_list(self, node):
619 619 self.list_start(node)
620 620
621 621 def depart_enumerated_list(self, node):
622 622 self.list_end()
623 623
624 624 def visit_error(self, node):
625 625 self.visit_admonition(node, 'error')
626 626
627 627 depart_error = depart_admonition
628 628
629 629 def visit_field(self, node):
630 630 pass
631 631
632 632 def depart_field(self, node):
633 633 pass
634 634
635 635 def visit_field_body(self, node):
636 636 if self._in_docinfo:
637 637 name_normalized = self._field_name.lower().replace(" ","_")
638 638 self._docinfo_names[name_normalized] = self._field_name
639 639 self.visit_docinfo_item(node, name_normalized)
640 640 raise nodes.SkipNode
641 641
642 642 def depart_field_body(self, node):
643 643 pass
644 644
645 645 def visit_field_list(self, node):
646 646 self.indent(FIELD_LIST_INDENT)
647 647
648 648 def depart_field_list(self, node):
649 649 self.dedent()
650 650
651 651 def visit_field_name(self, node):
652 652 if self._in_docinfo:
653 653 self._field_name = node.astext()
654 654 raise nodes.SkipNode
655 655 else:
656 656 self.body.append(self.defs['field_name'][0])
657 657
658 658 def depart_field_name(self, node):
659 659 self.body.append(self.defs['field_name'][1])
660 660
661 661 def visit_figure(self, node):
662 662 self.indent(2.5)
663 663 self.indent(0)
664 664
665 665 def depart_figure(self, node):
666 666 self.dedent()
667 667 self.dedent()
668 668
669 669 def visit_footer(self, node):
670 670 self.document.reporter.warning('"footer" not supported',
671 671 base_node=node)
672 672
673 673 def depart_footer(self, node):
674 674 pass
675 675
676 676 def visit_footnote(self, node):
677 677 num, text = node.astext().split(None, 1)
678 678 num = num.strip()
679 679 self.body.append('.IP [%s] 5\n' % self.deunicode(num))
680 680
681 681 def depart_footnote(self, node):
682 682 pass
683 683
684 684 def footnote_backrefs(self, node):
685 685 self.document.reporter.warning('"footnote_backrefs" not supported',
686 686 base_node=node)
687 687
688 688 def visit_footnote_reference(self, node):
689 689 self.body.append('['+self.deunicode(node.astext())+']')
690 690 raise nodes.SkipNode
691 691
692 692 def depart_footnote_reference(self, node):
693 693 pass
694 694
695 695 def visit_generated(self, node):
696 696 pass
697 697
698 698 def depart_generated(self, node):
699 699 pass
700 700
701 701 def visit_header(self, node):
702 702 raise NotImplementedError, node.astext()
703 703
704 704 def depart_header(self, node):
705 705 pass
706 706
707 707 def visit_hint(self, node):
708 708 self.visit_admonition(node, 'hint')
709 709
710 710 depart_hint = depart_admonition
711 711
712 712 def visit_subscript(self, node):
713 713 self.body.append('\\s-2\\d')
714 714
715 715 def depart_subscript(self, node):
716 716 self.body.append('\\u\\s0')
717 717
718 718 def visit_superscript(self, node):
719 719 self.body.append('\\s-2\\u')
720 720
721 721 def depart_superscript(self, node):
722 722 self.body.append('\\d\\s0')
723 723
724 724 def visit_attribution(self, node):
725 725 self.body.append('\\(em ')
726 726
727 727 def depart_attribution(self, node):
728 728 self.body.append('\n')
729 729
730 730 def visit_image(self, node):
731 731 self.document.reporter.warning('"image" not supported',
732 732 base_node=node)
733 733 text = []
734 734 if 'alt' in node.attributes:
735 735 text.append(node.attributes['alt'])
736 736 if 'uri' in node.attributes:
737 737 text.append(node.attributes['uri'])
738 738 self.body.append('[image: %s]\n' % ('/'.join(text)))
739 739 raise nodes.SkipNode
740 740
741 741 def visit_important(self, node):
742 742 self.visit_admonition(node, 'important')
743 743
744 744 depart_important = depart_admonition
745 745
746 746 def visit_label(self, node):
747 747 # footnote and citation
748 748 if (isinstance(node.parent, nodes.footnote)
749 749 or isinstance(node.parent, nodes.citation)):
750 750 raise nodes.SkipNode
751 751 self.document.reporter.warning('"unsupported "label"',
752 752 base_node=node)
753 753 self.body.append('[')
754 754
755 755 def depart_label(self, node):
756 756 self.body.append(']\n')
757 757
758 758 def visit_legend(self, node):
759 759 pass
760 760
761 761 def depart_legend(self, node):
762 762 pass
763 763
764 764 # WHAT should we use .INDENT, .UNINDENT ?
765 765 def visit_line_block(self, node):
766 766 self._line_block += 1
767 767 if self._line_block == 1:
768 768 self.body.append('.sp\n')
769 769 self.body.append('.nf\n')
770 770 else:
771 771 self.body.append('.in +2\n')
772 772
773 773 def depart_line_block(self, node):
774 774 self._line_block -= 1
775 775 if self._line_block == 0:
776 776 self.body.append('.fi\n')
777 777 self.body.append('.sp\n')
778 778 else:
779 779 self.body.append('.in -2\n')
780 780
781 781 def visit_line(self, node):
782 782 pass
783 783
784 784 def depart_line(self, node):
785 785 self.body.append('\n')
786 786
787 787 def visit_list_item(self, node):
788 788 # man 7 man argues to use ".IP" instead of ".TP"
789 789 self.body.append('.IP %s %d\n' % (
790 790 self._list_char[-1].next(),
791 791 self._list_char[-1].get_width(),))
792 792
793 793 def depart_list_item(self, node):
794 794 pass
795 795
796 796 def visit_literal(self, node):
797 797 self.body.append(self.defs['literal'][0])
798 798
799 799 def depart_literal(self, node):
800 800 self.body.append(self.defs['literal'][1])
801 801
802 802 def visit_literal_block(self, node):
803 803 self.body.append(self.defs['literal_block'][0])
804 804 self._in_literal = True
805 805
806 806 def depart_literal_block(self, node):
807 807 self._in_literal = False
808 808 self.body.append(self.defs['literal_block'][1])
809 809
810 810 def visit_meta(self, node):
811 811 raise NotImplementedError, node.astext()
812 812
813 813 def depart_meta(self, node):
814 814 pass
815 815
816 816 def visit_note(self, node):
817 817 self.visit_admonition(node, 'note')
818 818
819 819 depart_note = depart_admonition
820 820
821 821 def indent(self, by=0.5):
822 822 # if we are in a section ".SH" there already is a .RS
823 823 step = self._indent[-1]
824 824 self._indent.append(by)
825 825 self.body.append(self.defs['indent'][0] % step)
826 826
827 827 def dedent(self):
828 828 self._indent.pop()
829 829 self.body.append(self.defs['indent'][1])
830 830
831 831 def visit_option_list(self, node):
832 832 self.indent(OPTION_LIST_INDENT)
833 833
834 834 def depart_option_list(self, node):
835 835 self.dedent()
836 836
837 837 def visit_option_list_item(self, node):
838 838 # one item of the list
839 839 self.body.append(self.defs['option_list_item'][0])
840 840
841 841 def depart_option_list_item(self, node):
842 842 self.body.append(self.defs['option_list_item'][1])
843 843
844 844 def visit_option_group(self, node):
845 845 # as one option could have several forms it is a group
846 846 # options without parameter bold only, .B, -v
847 847 # options with parameter bold italic, .BI, -f file
848 848 #
849 849 # we do not know if .B or .BI
850 850 self.context.append('.B') # blind guess
851 851 self.context.append(len(self.body)) # to be able to insert later
852 852 self.context.append(0) # option counter
853 853
854 854 def depart_option_group(self, node):
855 855 self.context.pop() # the counter
856 856 start_position = self.context.pop()
857 857 text = self.body[start_position:]
858 858 del self.body[start_position:]
859 859 self.body.append('%s%s\n' % (self.context.pop(), ''.join(text)))
860 860
861 861 def visit_option(self, node):
862 862 # each form of the option will be presented separately
863 863 if self.context[-1] > 0:
864 864 self.body.append(', ')
865 865 if self.context[-3] == '.BI':
866 866 self.body.append('\\')
867 867 self.body.append(' ')
868 868
869 869 def depart_option(self, node):
870 870 self.context[-1] += 1
871 871
872 872 def visit_option_string(self, node):
873 873 # do not know if .B or .BI
874 874 pass
875 875
876 876 def depart_option_string(self, node):
877 877 pass
878 878
879 879 def visit_option_argument(self, node):
880 880 self.context[-3] = '.BI' # bold/italic alternate
881 881 if node['delimiter'] != ' ':
882 882 self.body.append('\\fB%s ' % node['delimiter'])
883 883 elif self.body[len(self.body)-1].endswith('='):
884 884 # a blank only means no blank in output, just changing font
885 885 self.body.append(' ')
886 886 else:
887 887 # blank backslash blank, switch font then a blank
888 888 self.body.append(' \\ ')
889 889
890 890 def depart_option_argument(self, node):
891 891 pass
892 892
893 893 def visit_organization(self, node):
894 894 self.visit_docinfo_item(node, 'organization')
895 895
896 896 def depart_organization(self, node):
897 897 pass
898 898
899 899 def visit_paragraph(self, node):
900 900 # ``.PP`` : Start standard indented paragraph.
901 901 # ``.LP`` : Start block paragraph, all except the first.
902 902 # ``.P [type]`` : Start paragraph type.
903 903 # NOTE don't use paragraph starts because they reset indentation.
904 904 # ``.sp`` is only vertical space
905 905 self.ensure_eol()
906 906 self.body.append('.sp\n')
907 907
908 908 def depart_paragraph(self, node):
909 909 self.body.append('\n')
910 910
911 911 def visit_problematic(self, node):
912 912 self.body.append(self.defs['problematic'][0])
913 913
914 914 def depart_problematic(self, node):
915 915 self.body.append(self.defs['problematic'][1])
916 916
917 917 def visit_raw(self, node):
918 918 if node.get('format') == 'manpage':
919 919 self.body.append(node.astext() + "\n")
920 920 # Keep non-manpage raw text out of output:
921 921 raise nodes.SkipNode
922 922
923 923 def visit_reference(self, node):
924 924 """E.g. link or email address."""
925 925 self.body.append(self.defs['reference'][0])
926 926
927 927 def depart_reference(self, node):
928 928 self.body.append(self.defs['reference'][1])
929 929
930 930 def visit_revision(self, node):
931 931 self.visit_docinfo_item(node, 'revision')
932 932
933 933 depart_revision = depart_docinfo_item
934 934
935 935 def visit_row(self, node):
936 936 self._active_table.new_row()
937 937
938 938 def depart_row(self, node):
939 939 pass
940 940
941 941 def visit_section(self, node):
942 942 self.section_level += 1
943 943
944 944 def depart_section(self, node):
945 945 self.section_level -= 1
946 946
947 947 def visit_status(self, node):
948 948 self.visit_docinfo_item(node, 'status')
949 949
950 950 depart_status = depart_docinfo_item
951 951
952 952 def visit_strong(self, node):
953 953 self.body.append(self.defs['strong'][0])
954 954
955 955 def depart_strong(self, node):
956 956 self.body.append(self.defs['strong'][1])
957 957
958 958 def visit_substitution_definition(self, node):
959 959 """Internal only."""
960 960 raise nodes.SkipNode
961 961
962 962 def visit_substitution_reference(self, node):
963 963 self.document.reporter.warning('"substitution_reference" not supported',
964 964 base_node=node)
965 965
966 966 def visit_subtitle(self, node):
967 967 if isinstance(node.parent, nodes.sidebar):
968 968 self.body.append(self.defs['strong'][0])
969 969 elif isinstance(node.parent, nodes.document):
970 970 self.visit_docinfo_item(node, 'subtitle')
971 971 elif isinstance(node.parent, nodes.section):
972 972 self.body.append(self.defs['strong'][0])
973 973
974 974 def depart_subtitle(self, node):
975 975 # document subtitle calls SkipNode
976 976 self.body.append(self.defs['strong'][1]+'\n.PP\n')
977 977
978 978 def visit_system_message(self, node):
979 979 # TODO add report_level
980 980 #if node['level'] < self.document.reporter['writer'].report_level:
981 981 # Level is too low to display:
982 982 # raise nodes.SkipNode
983 983 attr = {}
984 984 backref_text = ''
985 985 if node.hasattr('id'):
986 986 attr['name'] = node['id']
987 987 if node.hasattr('line'):
988 988 line = ', line %s' % node['line']
989 989 else:
990 990 line = ''
991 991 self.body.append('.IP "System Message: %s/%s (%s:%s)"\n'
992 992 % (node['type'], node['level'], node['source'], line))
993 993
994 994 def depart_system_message(self, node):
995 995 pass
996 996
997 997 def visit_table(self, node):
998 998 self._active_table = Table()
999 999
1000 1000 def depart_table(self, node):
1001 1001 self.ensure_eol()
1002 1002 self.body.extend(self._active_table.as_list())
1003 1003 self._active_table = None
1004 1004
1005 1005 def visit_target(self, node):
1006 1006 # targets are in-document hyper targets, without any use for man-pages.
1007 1007 raise nodes.SkipNode
1008 1008
1009 1009 def visit_tbody(self, node):
1010 1010 pass
1011 1011
1012 1012 def depart_tbody(self, node):
1013 1013 pass
1014 1014
1015 1015 def visit_term(self, node):
1016 1016 self.body.append(self.defs['term'][0])
1017 1017
1018 1018 def depart_term(self, node):
1019 1019 self.body.append(self.defs['term'][1])
1020 1020
1021 1021 def visit_tgroup(self, node):
1022 1022 pass
1023 1023
1024 1024 def depart_tgroup(self, node):
1025 1025 pass
1026 1026
1027 1027 def visit_thead(self, node):
1028 1028 # MAYBE double line '='
1029 1029 pass
1030 1030
1031 1031 def depart_thead(self, node):
1032 1032 # MAYBE double line '='
1033 1033 pass
1034 1034
1035 1035 def visit_tip(self, node):
1036 1036 self.visit_admonition(node, 'tip')
1037 1037
1038 1038 depart_tip = depart_admonition
1039 1039
1040 1040 def visit_title(self, node):
1041 1041 if isinstance(node.parent, nodes.topic):
1042 1042 self.body.append(self.defs['topic-title'][0])
1043 1043 elif isinstance(node.parent, nodes.sidebar):
1044 1044 self.body.append(self.defs['sidebar-title'][0])
1045 1045 elif isinstance(node.parent, nodes.admonition):
1046 1046 self.body.append('.IP "')
1047 1047 elif self.section_level == 0:
1048 1048 self._docinfo['title'] = node.astext()
1049 1049 # document title for .TH
1050 1050 self._docinfo['title_upper'] = node.astext().upper()
1051 1051 raise nodes.SkipNode
1052 1052 elif self.section_level == 1:
1053 1053 self.body.append('.SH ')
1054 1054 for n in node.traverse(nodes.Text):
1055 1055 n.parent.replace(n, nodes.Text(n.astext().upper()))
1056 1056 else:
1057 1057 self.body.append('.SS ')
1058 1058
1059 1059 def depart_title(self, node):
1060 1060 if isinstance(node.parent, nodes.admonition):
1061 1061 self.body.append('"')
1062 1062 self.body.append('\n')
1063 1063
1064 1064 def visit_title_reference(self, node):
1065 1065 """inline citation reference"""
1066 1066 self.body.append(self.defs['title_reference'][0])
1067 1067
1068 1068 def depart_title_reference(self, node):
1069 1069 self.body.append(self.defs['title_reference'][1])
1070 1070
1071 1071 def visit_topic(self, node):
1072 1072 pass
1073 1073
1074 1074 def depart_topic(self, node):
1075 1075 pass
1076 1076
1077 1077 def visit_sidebar(self, node):
1078 1078 pass
1079 1079
1080 1080 def depart_sidebar(self, node):
1081 1081 pass
1082 1082
1083 1083 def visit_rubric(self, node):
1084 1084 pass
1085 1085
1086 1086 def depart_rubric(self, node):
1087 1087 pass
1088 1088
1089 1089 def visit_transition(self, node):
1090 1090 # .PP Begin a new paragraph and reset prevailing indent.
1091 1091 # .sp N leaves N lines of blank space.
1092 1092 # .ce centers the next line
1093 1093 self.body.append('\n.sp\n.ce\n----\n')
1094 1094
1095 1095 def depart_transition(self, node):
1096 1096 self.body.append('\n.ce 0\n.sp\n')
1097 1097
1098 1098 def visit_version(self, node):
1099 1099 self.visit_docinfo_item(node, 'version')
1100 1100
1101 1101 def visit_warning(self, node):
1102 1102 self.visit_admonition(node, 'warning')
1103 1103
1104 1104 depart_warning = depart_admonition
1105 1105
1106 1106 def unimplemented_visit(self, node):
1107 1107 raise NotImplementedError('visiting unimplemented node type: %s'
1108 1108 % node.__class__.__name__)
1109 1109
1110 1110 # vim: set fileencoding=utf-8 et ts=4 ai :
General Comments 0
You need to be logged in to leave comments. Login now