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