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