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