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