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