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