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