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