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