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