##// END OF EJS Templates
minirst: pull admonition titles out formatblock function
Martin Geisler -
r12652:3c31c0e4 default
parent child Browse files
Show More
@@ -1,437 +1,437 b''
1 # minirst.py - minimal reStructuredText parser
1 # minirst.py - minimal reStructuredText parser
2 #
2 #
3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009, 2010 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 """simplified reStructuredText parser.
8 """simplified reStructuredText parser.
9
9
10 This parser knows just enough about reStructuredText to parse the
10 This parser knows just enough about reStructuredText to parse the
11 Mercurial docstrings.
11 Mercurial docstrings.
12
12
13 It cheats in a major way: nested blocks are not really nested. They
13 It cheats in a major way: nested blocks are not really nested. They
14 are just indented blocks that look like they are nested. This relies
14 are just indented blocks that look like they are nested. This relies
15 on the user to keep the right indentation for the blocks.
15 on the user to keep the right indentation for the blocks.
16
16
17 It only supports a small subset of reStructuredText:
17 It only supports a small subset of reStructuredText:
18
18
19 - sections
19 - sections
20
20
21 - paragraphs
21 - paragraphs
22
22
23 - literal blocks
23 - literal blocks
24
24
25 - definition lists
25 - definition lists
26
26
27 - specific admonitions
27 - specific admonitions
28
28
29 - bullet lists (items must start with '-')
29 - bullet lists (items must start with '-')
30
30
31 - enumerated lists (no autonumbering)
31 - enumerated lists (no autonumbering)
32
32
33 - field lists (colons cannot be escaped)
33 - field lists (colons cannot be escaped)
34
34
35 - option lists (supports only long options without arguments)
35 - option lists (supports only long options without arguments)
36
36
37 - inline literals (no other inline markup is not recognized)
37 - inline literals (no other inline markup is not recognized)
38 """
38 """
39
39
40 import re, sys
40 import re, sys
41 import util, encoding
41 import util, encoding
42 from i18n import _
42 from i18n import _
43
43
44
44
45 def replace(text, substs):
45 def replace(text, substs):
46 utext = text.decode(encoding.encoding)
46 utext = text.decode(encoding.encoding)
47 for f, t in substs:
47 for f, t in substs:
48 utext = utext.replace(f, t)
48 utext = utext.replace(f, t)
49 return utext.encode(encoding.encoding)
49 return utext.encode(encoding.encoding)
50
50
51
51
52 _blockre = re.compile(r"\n(?:\s*\n)+")
52 _blockre = re.compile(r"\n(?:\s*\n)+")
53
53
54 def findblocks(text):
54 def findblocks(text):
55 """Find continuous blocks of lines in text.
55 """Find continuous blocks of lines in text.
56
56
57 Returns a list of dictionaries representing the blocks. Each block
57 Returns a list of dictionaries representing the blocks. Each block
58 has an 'indent' field and a 'lines' field.
58 has an 'indent' field and a 'lines' field.
59 """
59 """
60 blocks = []
60 blocks = []
61 for b in _blockre.split(text.strip()):
61 for b in _blockre.split(text.strip()):
62 lines = b.splitlines()
62 lines = b.splitlines()
63 indent = min((len(l) - len(l.lstrip())) for l in lines)
63 indent = min((len(l) - len(l.lstrip())) for l in lines)
64 lines = [l[indent:] for l in lines]
64 lines = [l[indent:] for l in lines]
65 blocks.append(dict(indent=indent, lines=lines))
65 blocks.append(dict(indent=indent, lines=lines))
66 return blocks
66 return blocks
67
67
68
68
69 def findliteralblocks(blocks):
69 def findliteralblocks(blocks):
70 """Finds literal blocks and adds a 'type' field to the blocks.
70 """Finds literal blocks and adds a 'type' field to the blocks.
71
71
72 Literal blocks are given the type 'literal', all other blocks are
72 Literal blocks are given the type 'literal', all other blocks are
73 given type the 'paragraph'.
73 given type the 'paragraph'.
74 """
74 """
75 i = 0
75 i = 0
76 while i < len(blocks):
76 while i < len(blocks):
77 # Searching for a block that looks like this:
77 # Searching for a block that looks like this:
78 #
78 #
79 # +------------------------------+
79 # +------------------------------+
80 # | paragraph |
80 # | paragraph |
81 # | (ends with "::") |
81 # | (ends with "::") |
82 # +------------------------------+
82 # +------------------------------+
83 # +---------------------------+
83 # +---------------------------+
84 # | indented literal block |
84 # | indented literal block |
85 # +---------------------------+
85 # +---------------------------+
86 blocks[i]['type'] = 'paragraph'
86 blocks[i]['type'] = 'paragraph'
87 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
87 if blocks[i]['lines'][-1].endswith('::') and i + 1 < len(blocks):
88 indent = blocks[i]['indent']
88 indent = blocks[i]['indent']
89 adjustment = blocks[i + 1]['indent'] - indent
89 adjustment = blocks[i + 1]['indent'] - indent
90
90
91 if blocks[i]['lines'] == ['::']:
91 if blocks[i]['lines'] == ['::']:
92 # Expanded form: remove block
92 # Expanded form: remove block
93 del blocks[i]
93 del blocks[i]
94 i -= 1
94 i -= 1
95 elif blocks[i]['lines'][-1].endswith(' ::'):
95 elif blocks[i]['lines'][-1].endswith(' ::'):
96 # Partially minimized form: remove space and both
96 # Partially minimized form: remove space and both
97 # colons.
97 # colons.
98 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
98 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-3]
99 else:
99 else:
100 # Fully minimized form: remove just one colon.
100 # Fully minimized form: remove just one colon.
101 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
101 blocks[i]['lines'][-1] = blocks[i]['lines'][-1][:-1]
102
102
103 # List items are formatted with a hanging indent. We must
103 # List items are formatted with a hanging indent. We must
104 # correct for this here while we still have the original
104 # correct for this here while we still have the original
105 # information on the indentation of the subsequent literal
105 # information on the indentation of the subsequent literal
106 # blocks available.
106 # blocks available.
107 m = _bulletre.match(blocks[i]['lines'][0])
107 m = _bulletre.match(blocks[i]['lines'][0])
108 if m:
108 if m:
109 indent += m.end()
109 indent += m.end()
110 adjustment -= m.end()
110 adjustment -= m.end()
111
111
112 # Mark the following indented blocks.
112 # Mark the following indented blocks.
113 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
113 while i + 1 < len(blocks) and blocks[i + 1]['indent'] > indent:
114 blocks[i + 1]['type'] = 'literal'
114 blocks[i + 1]['type'] = 'literal'
115 blocks[i + 1]['indent'] -= adjustment
115 blocks[i + 1]['indent'] -= adjustment
116 i += 1
116 i += 1
117 i += 1
117 i += 1
118 return blocks
118 return blocks
119
119
120 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
120 _bulletre = re.compile(r'(-|[0-9A-Za-z]+\.|\(?[0-9A-Za-z]+\)|\|) ')
121 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
121 _optionre = re.compile(r'^(--[a-z-]+)((?:[ =][a-zA-Z][\w-]*)? +)(.*)$')
122 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
122 _fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
123 _definitionre = re.compile(r'[^ ]')
123 _definitionre = re.compile(r'[^ ]')
124
124
125 def splitparagraphs(blocks):
125 def splitparagraphs(blocks):
126 """Split paragraphs into lists."""
126 """Split paragraphs into lists."""
127 # Tuples with (list type, item regexp, single line items?). Order
127 # Tuples with (list type, item regexp, single line items?). Order
128 # matters: definition lists has the least specific regexp and must
128 # matters: definition lists has the least specific regexp and must
129 # come last.
129 # come last.
130 listtypes = [('bullet', _bulletre, True),
130 listtypes = [('bullet', _bulletre, True),
131 ('option', _optionre, True),
131 ('option', _optionre, True),
132 ('field', _fieldre, True),
132 ('field', _fieldre, True),
133 ('definition', _definitionre, False)]
133 ('definition', _definitionre, False)]
134
134
135 def match(lines, i, itemre, singleline):
135 def match(lines, i, itemre, singleline):
136 """Does itemre match an item at line i?
136 """Does itemre match an item at line i?
137
137
138 A list item can be followed by an idented line or another list
138 A list item can be followed by an idented line or another list
139 item (but only if singleline is True).
139 item (but only if singleline is True).
140 """
140 """
141 line1 = lines[i]
141 line1 = lines[i]
142 line2 = i + 1 < len(lines) and lines[i + 1] or ''
142 line2 = i + 1 < len(lines) and lines[i + 1] or ''
143 if not itemre.match(line1):
143 if not itemre.match(line1):
144 return False
144 return False
145 if singleline:
145 if singleline:
146 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
146 return line2 == '' or line2[0] == ' ' or itemre.match(line2)
147 else:
147 else:
148 return line2.startswith(' ')
148 return line2.startswith(' ')
149
149
150 i = 0
150 i = 0
151 while i < len(blocks):
151 while i < len(blocks):
152 if blocks[i]['type'] == 'paragraph':
152 if blocks[i]['type'] == 'paragraph':
153 lines = blocks[i]['lines']
153 lines = blocks[i]['lines']
154 for type, itemre, singleline in listtypes:
154 for type, itemre, singleline in listtypes:
155 if match(lines, 0, itemre, singleline):
155 if match(lines, 0, itemre, singleline):
156 items = []
156 items = []
157 for j, line in enumerate(lines):
157 for j, line in enumerate(lines):
158 if match(lines, j, itemre, singleline):
158 if match(lines, j, itemre, singleline):
159 items.append(dict(type=type, lines=[],
159 items.append(dict(type=type, lines=[],
160 indent=blocks[i]['indent']))
160 indent=blocks[i]['indent']))
161 items[-1]['lines'].append(line)
161 items[-1]['lines'].append(line)
162 blocks[i:i + 1] = items
162 blocks[i:i + 1] = items
163 break
163 break
164 i += 1
164 i += 1
165 return blocks
165 return blocks
166
166
167
167
168 _fieldwidth = 12
168 _fieldwidth = 12
169
169
170 def updatefieldlists(blocks):
170 def updatefieldlists(blocks):
171 """Find key and maximum key width for field lists."""
171 """Find key and maximum key width for field lists."""
172 i = 0
172 i = 0
173 while i < len(blocks):
173 while i < len(blocks):
174 if blocks[i]['type'] != 'field':
174 if blocks[i]['type'] != 'field':
175 i += 1
175 i += 1
176 continue
176 continue
177
177
178 keywidth = 0
178 keywidth = 0
179 j = i
179 j = i
180 while j < len(blocks) and blocks[j]['type'] == 'field':
180 while j < len(blocks) and blocks[j]['type'] == 'field':
181 m = _fieldre.match(blocks[j]['lines'][0])
181 m = _fieldre.match(blocks[j]['lines'][0])
182 key, rest = m.groups()
182 key, rest = m.groups()
183 blocks[j]['lines'][0] = rest
183 blocks[j]['lines'][0] = rest
184 blocks[j]['key'] = key
184 blocks[j]['key'] = key
185 keywidth = max(keywidth, len(key))
185 keywidth = max(keywidth, len(key))
186 j += 1
186 j += 1
187
187
188 for block in blocks[i:j]:
188 for block in blocks[i:j]:
189 block['keywidth'] = keywidth
189 block['keywidth'] = keywidth
190 i = j + 1
190 i = j + 1
191
191
192 return blocks
192 return blocks
193
193
194
194
195 def prunecontainers(blocks, keep):
195 def prunecontainers(blocks, keep):
196 """Prune unwanted containers.
196 """Prune unwanted containers.
197
197
198 The blocks must have a 'type' field, i.e., they should have been
198 The blocks must have a 'type' field, i.e., they should have been
199 run through findliteralblocks first.
199 run through findliteralblocks first.
200 """
200 """
201 pruned = []
201 pruned = []
202 i = 0
202 i = 0
203 while i + 1 < len(blocks):
203 while i + 1 < len(blocks):
204 # Searching for a block that looks like this:
204 # Searching for a block that looks like this:
205 #
205 #
206 # +-------+---------------------------+
206 # +-------+---------------------------+
207 # | ".. container ::" type |
207 # | ".. container ::" type |
208 # +---+ |
208 # +---+ |
209 # | blocks |
209 # | blocks |
210 # +-------------------------------+
210 # +-------------------------------+
211 if (blocks[i]['type'] == 'paragraph' and
211 if (blocks[i]['type'] == 'paragraph' and
212 blocks[i]['lines'][0].startswith('.. container::')):
212 blocks[i]['lines'][0].startswith('.. container::')):
213 indent = blocks[i]['indent']
213 indent = blocks[i]['indent']
214 adjustment = blocks[i + 1]['indent'] - indent
214 adjustment = blocks[i + 1]['indent'] - indent
215 containertype = blocks[i]['lines'][0][15:]
215 containertype = blocks[i]['lines'][0][15:]
216 prune = containertype not in keep
216 prune = containertype not in keep
217 if prune:
217 if prune:
218 pruned.append(containertype)
218 pruned.append(containertype)
219
219
220 # Always delete "..container:: type" block
220 # Always delete "..container:: type" block
221 del blocks[i]
221 del blocks[i]
222 j = i
222 j = i
223 while j < len(blocks) and blocks[j]['indent'] > indent:
223 while j < len(blocks) and blocks[j]['indent'] > indent:
224 if prune:
224 if prune:
225 del blocks[j]
225 del blocks[j]
226 i -= 1 # adjust outer index
226 i -= 1 # adjust outer index
227 else:
227 else:
228 blocks[j]['indent'] -= adjustment
228 blocks[j]['indent'] -= adjustment
229 j += 1
229 j += 1
230 i += 1
230 i += 1
231 return blocks, pruned
231 return blocks, pruned
232
232
233
233
234 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
234 _sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
235
235
236 def findsections(blocks):
236 def findsections(blocks):
237 """Finds sections.
237 """Finds sections.
238
238
239 The blocks must have a 'type' field, i.e., they should have been
239 The blocks must have a 'type' field, i.e., they should have been
240 run through findliteralblocks first.
240 run through findliteralblocks first.
241 """
241 """
242 for block in blocks:
242 for block in blocks:
243 # Searching for a block that looks like this:
243 # Searching for a block that looks like this:
244 #
244 #
245 # +------------------------------+
245 # +------------------------------+
246 # | Section title |
246 # | Section title |
247 # | ------------- |
247 # | ------------- |
248 # +------------------------------+
248 # +------------------------------+
249 if (block['type'] == 'paragraph' and
249 if (block['type'] == 'paragraph' and
250 len(block['lines']) == 2 and
250 len(block['lines']) == 2 and
251 len(block['lines'][0]) == len(block['lines'][1]) and
251 len(block['lines'][0]) == len(block['lines'][1]) and
252 _sectionre.match(block['lines'][1])):
252 _sectionre.match(block['lines'][1])):
253 block['underline'] = block['lines'][1][0]
253 block['underline'] = block['lines'][1][0]
254 block['type'] = 'section'
254 block['type'] = 'section'
255 del block['lines'][1]
255 del block['lines'][1]
256 return blocks
256 return blocks
257
257
258
258
259 def inlineliterals(blocks):
259 def inlineliterals(blocks):
260 substs = [('``', '"')]
260 substs = [('``', '"')]
261 for b in blocks:
261 for b in blocks:
262 if b['type'] in ('paragraph', 'section'):
262 if b['type'] in ('paragraph', 'section'):
263 b['lines'] = [replace(l, substs) for l in b['lines']]
263 b['lines'] = [replace(l, substs) for l in b['lines']]
264 return blocks
264 return blocks
265
265
266
266
267 def hgrole(blocks):
267 def hgrole(blocks):
268 substs = [(':hg:`', '"hg '), ('`', '"')]
268 substs = [(':hg:`', '"hg '), ('`', '"')]
269 for b in blocks:
269 for b in blocks:
270 if b['type'] in ('paragraph', 'section'):
270 if b['type'] in ('paragraph', 'section'):
271 # Turn :hg:`command` into "hg command". This also works
271 # Turn :hg:`command` into "hg command". This also works
272 # when there is a line break in the command and relies on
272 # when there is a line break in the command and relies on
273 # the fact that we have no stray back-quotes in the input
273 # the fact that we have no stray back-quotes in the input
274 # (run the blocks through inlineliterals first).
274 # (run the blocks through inlineliterals first).
275 b['lines'] = [replace(l, substs) for l in b['lines']]
275 b['lines'] = [replace(l, substs) for l in b['lines']]
276 return blocks
276 return blocks
277
277
278
278
279 def addmargins(blocks):
279 def addmargins(blocks):
280 """Adds empty blocks for vertical spacing.
280 """Adds empty blocks for vertical spacing.
281
281
282 This groups bullets, options, and definitions together with no vertical
282 This groups bullets, options, and definitions together with no vertical
283 space between them, and adds an empty block between all other blocks.
283 space between them, and adds an empty block between all other blocks.
284 """
284 """
285 i = 1
285 i = 1
286 while i < len(blocks):
286 while i < len(blocks):
287 if (blocks[i]['type'] == blocks[i - 1]['type'] and
287 if (blocks[i]['type'] == blocks[i - 1]['type'] and
288 blocks[i]['type'] in ('bullet', 'option', 'field')):
288 blocks[i]['type'] in ('bullet', 'option', 'field')):
289 i += 1
289 i += 1
290 else:
290 else:
291 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
291 blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
292 i += 2
292 i += 2
293 return blocks
293 return blocks
294
294
295 _admonitionre = re.compile(r"\.\. (admonition|attention|caution|danger|"
295 _admonitionre = re.compile(r"\.\. (admonition|attention|caution|danger|"
296 r"error|hint|important|note|tip|warning)::",
296 r"error|hint|important|note|tip|warning)::",
297 flags=re.IGNORECASE)
297 flags=re.IGNORECASE)
298
298
299 def findadmonitions(blocks):
299 def findadmonitions(blocks):
300 """
300 """
301 Makes the type of the block an admonition block if
301 Makes the type of the block an admonition block if
302 the first line is an admonition directive
302 the first line is an admonition directive
303 """
303 """
304 i = 0
304 i = 0
305 while i < len(blocks):
305 while i < len(blocks):
306 m = _admonitionre.match(blocks[i]['lines'][0])
306 m = _admonitionre.match(blocks[i]['lines'][0])
307 if m:
307 if m:
308 blocks[i]['type'] = 'admonition'
308 blocks[i]['type'] = 'admonition'
309 admonitiontitle = blocks[i]['lines'][0][3:m.end() - 2].lower()
309 admonitiontitle = blocks[i]['lines'][0][3:m.end() - 2].lower()
310
310
311 firstline = blocks[i]['lines'][0][m.end() + 1:]
311 firstline = blocks[i]['lines'][0][m.end() + 1:]
312 if firstline:
312 if firstline:
313 blocks[i]['lines'].insert(1, ' ' + firstline)
313 blocks[i]['lines'].insert(1, ' ' + firstline)
314
314
315 blocks[i]['admonitiontitle'] = admonitiontitle
315 blocks[i]['admonitiontitle'] = admonitiontitle
316 del blocks[i]['lines'][0]
316 del blocks[i]['lines'][0]
317 i = i + 1
317 i = i + 1
318 return blocks
318 return blocks
319
319
320 def formatblock(block, width):
320 _admonitiontitles = {'attention': _('Attention:'),
321 """Format a block according to width."""
322 if width <= 0:
323 width = 78
324 indent = ' ' * block['indent']
325 if block['type'] == 'admonition':
326 titles = {'attention': _('Attention:'),
327 'caution': _('Caution:'),
321 'caution': _('Caution:'),
328 'danger': _('!Danger!') ,
322 'danger': _('!Danger!') ,
329 'error': _('Error:'),
323 'error': _('Error:'),
330 'hint': _('Hint:'),
324 'hint': _('Hint:'),
331 'important': _('Important:'),
325 'important': _('Important:'),
332 'note': _('Note:'),
326 'note': _('Note:'),
333 'tip': _('Tip:'),
327 'tip': _('Tip:'),
334 'warning': _('Warning!')}
328 'warning': _('Warning!')}
335
329
336 admonition = titles[block['admonitiontitle']]
330 def formatblock(block, width):
331 """Format a block according to width."""
332 if width <= 0:
333 width = 78
334 indent = ' ' * block['indent']
335 if block['type'] == 'admonition':
336 admonition = _admonitiontitles[block['admonitiontitle']]
337 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
337 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
338
338
339 defindent = indent + hang * ' '
339 defindent = indent + hang * ' '
340 text = ' '.join(map(str.strip, block['lines']))
340 text = ' '.join(map(str.strip, block['lines']))
341 return '%s\n%s' % (indent + admonition, util.wrap(text, width=width,
341 return '%s\n%s' % (indent + admonition, util.wrap(text, width=width,
342 initindent=defindent,
342 initindent=defindent,
343 hangindent=defindent))
343 hangindent=defindent))
344 if block['type'] == 'margin':
344 if block['type'] == 'margin':
345 return ''
345 return ''
346 if block['type'] == 'literal':
346 if block['type'] == 'literal':
347 indent += ' '
347 indent += ' '
348 return indent + ('\n' + indent).join(block['lines'])
348 return indent + ('\n' + indent).join(block['lines'])
349 if block['type'] == 'section':
349 if block['type'] == 'section':
350 underline = len(block['lines'][0]) * block['underline']
350 underline = len(block['lines'][0]) * block['underline']
351 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
351 return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
352 if block['type'] == 'definition':
352 if block['type'] == 'definition':
353 term = indent + block['lines'][0]
353 term = indent + block['lines'][0]
354 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
354 hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
355 defindent = indent + hang * ' '
355 defindent = indent + hang * ' '
356 text = ' '.join(map(str.strip, block['lines'][1:]))
356 text = ' '.join(map(str.strip, block['lines'][1:]))
357 return '%s\n%s' % (term, util.wrap(text, width=width,
357 return '%s\n%s' % (term, util.wrap(text, width=width,
358 initindent=defindent,
358 initindent=defindent,
359 hangindent=defindent))
359 hangindent=defindent))
360 subindent = indent
360 subindent = indent
361 if block['type'] == 'bullet':
361 if block['type'] == 'bullet':
362 if block['lines'][0].startswith('| '):
362 if block['lines'][0].startswith('| '):
363 # Remove bullet for line blocks and add no extra
363 # Remove bullet for line blocks and add no extra
364 # indention.
364 # indention.
365 block['lines'][0] = block['lines'][0][2:]
365 block['lines'][0] = block['lines'][0][2:]
366 else:
366 else:
367 m = _bulletre.match(block['lines'][0])
367 m = _bulletre.match(block['lines'][0])
368 subindent = indent + m.end() * ' '
368 subindent = indent + m.end() * ' '
369 elif block['type'] == 'field':
369 elif block['type'] == 'field':
370 keywidth = block['keywidth']
370 keywidth = block['keywidth']
371 key = block['key']
371 key = block['key']
372
372
373 subindent = indent + _fieldwidth * ' '
373 subindent = indent + _fieldwidth * ' '
374 if len(key) + 2 > _fieldwidth:
374 if len(key) + 2 > _fieldwidth:
375 # key too large, use full line width
375 # key too large, use full line width
376 key = key.ljust(width)
376 key = key.ljust(width)
377 elif keywidth + 2 < _fieldwidth:
377 elif keywidth + 2 < _fieldwidth:
378 # all keys are small, add only two spaces
378 # all keys are small, add only two spaces
379 key = key.ljust(keywidth + 2)
379 key = key.ljust(keywidth + 2)
380 subindent = indent + (keywidth + 2) * ' '
380 subindent = indent + (keywidth + 2) * ' '
381 else:
381 else:
382 # mixed sizes, use fieldwidth for this one
382 # mixed sizes, use fieldwidth for this one
383 key = key.ljust(_fieldwidth)
383 key = key.ljust(_fieldwidth)
384 block['lines'][0] = key + block['lines'][0]
384 block['lines'][0] = key + block['lines'][0]
385 elif block['type'] == 'option':
385 elif block['type'] == 'option':
386 m = _optionre.match(block['lines'][0])
386 m = _optionre.match(block['lines'][0])
387 option, arg, rest = m.groups()
387 option, arg, rest = m.groups()
388 subindent = indent + (len(option) + len(arg)) * ' '
388 subindent = indent + (len(option) + len(arg)) * ' '
389
389
390 text = ' '.join(map(str.strip, block['lines']))
390 text = ' '.join(map(str.strip, block['lines']))
391 return util.wrap(text, width=width,
391 return util.wrap(text, width=width,
392 initindent=indent,
392 initindent=indent,
393 hangindent=subindent)
393 hangindent=subindent)
394
394
395
395
396 def format(text, width, indent=0, keep=None):
396 def format(text, width, indent=0, keep=None):
397 """Parse and format the text according to width."""
397 """Parse and format the text according to width."""
398 blocks = findblocks(text)
398 blocks = findblocks(text)
399 for b in blocks:
399 for b in blocks:
400 b['indent'] += indent
400 b['indent'] += indent
401 blocks = findliteralblocks(blocks)
401 blocks = findliteralblocks(blocks)
402 blocks, pruned = prunecontainers(blocks, keep or [])
402 blocks, pruned = prunecontainers(blocks, keep or [])
403 blocks = findsections(blocks)
403 blocks = findsections(blocks)
404 blocks = inlineliterals(blocks)
404 blocks = inlineliterals(blocks)
405 blocks = hgrole(blocks)
405 blocks = hgrole(blocks)
406 blocks = splitparagraphs(blocks)
406 blocks = splitparagraphs(blocks)
407 blocks = updatefieldlists(blocks)
407 blocks = updatefieldlists(blocks)
408 blocks = addmargins(blocks)
408 blocks = addmargins(blocks)
409 blocks = findadmonitions(blocks)
409 blocks = findadmonitions(blocks)
410 text = '\n'.join(formatblock(b, width) for b in blocks)
410 text = '\n'.join(formatblock(b, width) for b in blocks)
411 if keep is None:
411 if keep is None:
412 return text
412 return text
413 else:
413 else:
414 return text, pruned
414 return text, pruned
415
415
416
416
417 if __name__ == "__main__":
417 if __name__ == "__main__":
418 from pprint import pprint
418 from pprint import pprint
419
419
420 def debug(func, *args):
420 def debug(func, *args):
421 blocks = func(*args)
421 blocks = func(*args)
422 print "*** after %s:" % func.__name__
422 print "*** after %s:" % func.__name__
423 pprint(blocks)
423 pprint(blocks)
424 print
424 print
425 return blocks
425 return blocks
426
426
427 text = open(sys.argv[1]).read()
427 text = open(sys.argv[1]).read()
428 blocks = debug(findblocks, text)
428 blocks = debug(findblocks, text)
429 blocks = debug(findliteralblocks, blocks)
429 blocks = debug(findliteralblocks, blocks)
430 blocks, pruned = debug(prunecontainers, blocks, sys.argv[2:])
430 blocks, pruned = debug(prunecontainers, blocks, sys.argv[2:])
431 blocks = debug(inlineliterals, blocks)
431 blocks = debug(inlineliterals, blocks)
432 blocks = debug(splitparagraphs, blocks)
432 blocks = debug(splitparagraphs, blocks)
433 blocks = debug(updatefieldlists, blocks)
433 blocks = debug(updatefieldlists, blocks)
434 blocks = debug(findsections, blocks)
434 blocks = debug(findsections, blocks)
435 blocks = debug(addmargins, blocks)
435 blocks = debug(addmargins, blocks)
436 blocks = debug(findadmonitions, blocks)
436 blocks = debug(findadmonitions, blocks)
437 print '\n'.join(formatblock(b, 30) for b in blocks)
437 print '\n'.join(formatblock(b, 30) for b in blocks)
General Comments 0
You need to be logged in to leave comments. Login now