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