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